spring boot整合shiro

摘要:
--shiro--˃org.apache.shiroshiro-core1.3.2˃org.apache.shero˃shiro-spring2.3.2˂!

安全框架Shiro和Spring Security比较,本文主要围绕Shiro进行学习

一 Shiro 是一个强大而灵活的开源安全框架,能够清晰的处理认证 授权 管理会话以及,密码加密

  01 .认证与授权相关概念

    安全实体:  系统需要保护的具体对象数据

    权限: 系统相关的功能操作,例如基本的CRUD

    Authentication:身份认证授权/登录,验证用户是否拥有相应的身份

    Authorization:  授权,即权限的认证,认证某个已认证的用户是否拥有某个权限

    Session  Manager : 会话管理.即用户登录后就是一次会话,在没有退出之前,所有信息都在会话中

    Cryptography:  加密,保护数据的安全性

    Web Support:web支持

    Caching:  缓存

    Concurrency:  shiro支持多线程并发验证,一个线程中开启另一个线程,能把权限自动传播过去:

    Remember Me:记住我

  02. shiro四大核心,.如下是shiro架构

 spring boot整合shiro第1张

  03 Shiro三个核心组件:Subject  .SecurityManager和Realms

    Subject:  主体,代表当前"用户",所有Subject都绑定到SecurityManager,是一个抽象的概念,与当前应用交互任何东西都是Subject,如网络爬虫,机器人,与Subject的所有交互都会委托给SecurityManager;

    SecurityManager:安全管理器:  即所有与安全有关的操作都会与SecuityManager交互,管理所有的Subject;是Shiro的核心,它负责与后边介绍的其他组件进行交互

    Realm:  域 , shiro从Realm获取安全数据(如 用户,角色,权限)SecurityManager要验证用户身份,那么他需要从Realm获取相应的用户,已确定使用户身份是否合法,也需要从Realm得到用户相应的角色/权限进行验证用于是否能进行操作,可以吧Realm看成DataSource.,即安全数据源

                   spring boot整合shiro第2张

  04  以下已springboot的一个项目中shiro运用为例

   下面是pom.xml中相关jar 

<!--shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- shiro ehcache -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>1.2.1</version>
        </dependency>

ShiroConfig配置文件

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.prostate.common.config.Constant;
import com.prostate.common.redis.shiro.RedisCacheManager;
import com.prostate.common.redis.shiro.RedisManager;
import com.prostate.common.redis.shiro.RedisSessionDAO;
import com.prostate.system.shiro.UserRealm;
//import org.apache.shiro.cache.CacheManager;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;

/**
 * @author*/

@Configuration public class ShiroConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.cache.type}") private String cacheType; @Value("${server.session-timeout}") private int tomcatTimeout; // @Autowired // CacheManager cacheManager; @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean * * @return */ @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } // ShiroFilterFactoryBean 为了生成ShiroFilter,处理拦截资源文件问题
//它主要保持三项数据,securityManager,filters,fiterChainDefinition
// Shiro验证URL时,匹配成功就不在匹配,自上而下
// @Bean ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean
= new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/index"); shiroFilterFactoryBean.setUnauthorizedUrl("/403"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/fonts/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); filterChainDefinitionMap.put("/docs/**", "anon"); filterChainDefinitionMap.put("/druid/**", "anon"); filterChainDefinitionMap.put("/upload/**", "anon"); filterChainDefinitionMap.put("/files/**", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/blog", "anon"); filterChainDefinitionMap.put("/blog/open/**", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm. securityManager.setRealm(userRealm()); // 自定义缓存实现 使用redis if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) { securityManager.setCacheManager(cacheManager()); } else { securityManager.setCacheManager(ehCacheManager()); } securityManager.setSessionManager(sessionManager()); return securityManager; } @Bean UserRealm userRealm() { UserRealm userRealm = new UserRealm(); return userRealm; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 配置shiro redisManager * * @return */ @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setExpire(1800);// 配置缓存过期时间 //redisManager.setTimeout(1800); redisManager.setPassword(password); return redisManager; } /** * cacheManager 缓存 redis实现 * 使用的是shiro-redis开源插件 * * @return */ public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * RedisSessionDAO shiro sessionDao层的实现 通过redis * 使用的是shiro-redis开源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } @Bean public SessionDAO sessionDAO() { if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) { return redisSessionDAO(); } else { return new MemorySessionDAO(); } } /** * shiro session的管理 */ @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000); sessionManager.setSessionDAO(sessionDAO()); Collection<SessionListener> listeners = new ArrayList<SessionListener>(); listeners.add(new BDSessionListener()); sessionManager.setSessionListeners(listeners); return sessionManager; } @Bean public EhCacheManager ehCacheManager() { EhCacheManager em = new EhCacheManager(); em.setCacheManager(CacheManager.create()); return em; } }

 这里补充一下ehcache和redis比较

  ehcache直接在jvm虚拟机中缓存,速度快,效率高,但是缓存共享麻烦,集群分布式应用不方便

   redis是通过socket访问缓存务,效率比ehcachedi,处理集群和分布式缓存方便,有成熟的方案

UserRealm

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.prostate.common.config.ApplicationContextRegister;
import com.prostate.system.domain.UserToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import com.prostate.common.utils.ShiroUtils;
import com.prostate.system.dao.UserDao;
import com.prostate.system.domain.UserDO;
import com.prostate.system.service.MenuService;

/**
 * 自定义realm 安全的数据库
 */
public class UserRealm extends AuthorizingRealm {
/*    @Autowired
    UserDao userMapper;
    @Autowired
    MenuService menuService;*/

    /**
     *
     * @param arg0
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        Long userId = ShiroUtils.getUserId();
        MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
        Set<String> perms = menuService.listPerms(userId);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(perms);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        Map<String, Object> map = new HashMap<>(16);
        map.put("username", username);
        String password = new String((char[]) token.getCredentials());

        UserDao userMapper = ApplicationContextRegister.getBean(UserDao.class);
        // 查询用户信息
        UserDO user = userMapper.list(map).get(0);

        // 账号不存在
        if (user == null) {
            throw new UnknownAccountException("账号或密码不正确");
        }

        // 密码错误
        if (!password.equals(user.getPassword())) {
            throw new IncorrectCredentialsException("账号或密码不正确");
        }

        // 账号锁定
        if (user.getStatus() == 0) {
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
        return info;
    }

}

ShiroUtils

package com.prostate.common.utils;

import com.prostate.system.domain.UserToken;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.Subject;

import com.prostate.system.domain.UserDO;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.InvocationTargetException;
import java.security.Principal;
import java.util.Collection;
import java.util.List;

public class ShiroUtils {
    @Autowired
    private static SessionDAO sessionDAO;

    public static Subject getSubjct() {
        return SecurityUtils.getSubject();
    }
    public static UserDO getUser() {
        Object object = getSubjct().getPrincipal();
        return (UserDO)object;
    }
    public static Long getUserId() {
        return getUser().getUserId();
    }
    public static void logout() {
        getSubjct().logout();
    }

    public static List<Principal> getPrinciples() {
        List<Principal> principals = null;
        Collection<Session> sessions = sessionDAO.getActiveSessions();
        return principals;
    }
}

Shiro 分析

①:  Shiro常见的抛出异常

UnknownAccountException(账号不存在)
IncorrectCredentialsException(密码不存在)
DisabledAccountException(帐号被禁用)
LockedAccountException(帐号被锁定)
ExcessiveAttemptsException(登录失败次数过多)
ExpiredCredentialsException(凭证过期)等

②:Shiro标签  

<shiro:authenticated> 登录之后
<shiro:notAuthenticated> 不在登录状态时
<shiro:guest> 用户在没有RememberMe时
<shiro:user> 用户在RememberMe时
<shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色时
<shiro:hasRole name="abc"> 拥有角色abc
<shiro:lacksRole name="abc"> 没有角色abc
<shiro:hasPermission name="abc"> 拥有权限abc
<shiro:lacksPermission name="abc"> 没有权限abc
<shiro:principal> 显示用户登录名

③ Shiro过滤器链中的几种内置过滤器

--(1)认证过滤器
anon              匿名过滤器
authc             需要认证
authcBasic     表示httpBasic认证  
user               表示必须存在用户,当登入操作时不做检查

--(2)授权过滤器
roles            /admins/user/**=authc,roles[admin] (必须认证过,并拥有admin 角色)
perms
port
rest
ssl 

--(3) shiro 登录退出
shiro 内置的logout 过滤器(先配置logout,再在过滤器链中配置)
<bean   class="org.apache.shiro.web.filter.authc.LogoutFilter"> 
        <property name="redirectUrl" value="/loginform" /> 
</bean>   
filterChainDefinitions 配置  /logout = logout 

--(4) 关于登录过滤器的配置
通常将登录请求和使用的资源(js.css等)放开设置为anon,将其他的所有资源/** 设置为authc

--(5)关于Session 失效的问题
每次请求都会经过过滤器链的过滤,对于authc的资源,如果登录失效,自动返回到默认登录首页 配置的loginUrl 中;
或者可以自定义个session 拦截的过滤器放入shiro 的过滤器链

二  Spring Security是一个能够为Spring的企业应用系统提供声明式安全访问控制解决方案的安全框架.它提供一组Spring应用上下文中配置Bean,充分利用Spring Ioc,DI,AOP,减少了为企业系统安全控制编写大量重复代码,它是一个轻量级的安全框架,他确保基于Spring的应用程序提供身份验证和授权支持,并配备了流行的安全算法实现捆绑在一起

后续详解  Spring Security

     

免责声明:文章转载自《spring boot整合shiro》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇从零开始手写 dubbo rpc 框架mybatis 遇到空串无法判断下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

flask设置cookie,设置session,模拟用户认证、模拟管理后台admin、模拟用户logout --

设置cookie HTTP协议是无状态的,在一次请求响应结束后,服务器不会留下关于客户端状态的信息。但是对于某些web程序来说,客户端的信息有必要被记住,比如用户的登录状态,这样就可以根据用户的状态来返回不同的响应。为了解决这个问题就有cookie技术 。cookie技术通过在请求和响应报文中添加cookie数据来保存客户端的状态信息。 cookie值we...

c#web中定义全局变量,传递变量

c# web开发中,定义全局变量是经常用到的.我的做法是 1\在一个webform 中, public static int aaa;  public static string bbb; //最简单的定义全局变量的方法 如果想在各个web form  用到传递 全局变量,则在 类文件中定义变量 public static int  abc; //最简单的...

SQLAlchemy+Flask-RESTful使用(四)

前言 顺利出到4啦,其实学习过程中发现了不少错误,不过有些实在是没啥代表性. 最近买了两本小程序和安卓方面的书,其实从初中开始,想搞编程的目的就是写些安卓软件. 现在看来不太可能了.拿来当当兴趣爱好还是没问题的 这几天不是没更新,是在前面的章节里增加/勘误 变更记录 # 19.4.15  起笔 # 19.4.15  增加 Flask-RESTful 获取u...

详细分析MySQL的日志(一)

官方手册:https://dev.mysql.com/doc/refman/5.7/en/server-logs.html 不管是哪个数据库产品,一定会有日志文件。在MariaDB/MySQL中,主要有5种日志文件: 1.错误日志(error log):记录mysql服务的启停时正确和错误的信息,还记录启动、停止、运行过程中的错误信息。 2.查询日志(ge...

10分钟搭建服务器集群——Windows7系统中nginx与IIS服务器搭建集群实现负载均衡

分布式,集群,云计算机、大数据、负载均衡、高并发······当耳边响起这些词时,做为一个菜鸟程序猿无疑心中会激动一番(或许这是判断是否是一个标准阿猿的标准吧)! 首先自己从宏观把控一下,通过上网科普自己遇到问题,然后再站在这些巨人的肩膀上解决问题。 问题:在大型WEB应用程系统中,由于请求的数量过大及并发的因素,导致Web系统会出现宕机的现象! 解决方案主...

JAVA代码时间SFTP文件的下载

参考文章:http://blog.csdn.net/smallerpig/article/details/50976191 SFTP文件的下载与FTP文件的下载差别较大,需要下载jsch-0.1.54jar包 package com.cd.util; import com.jcraft.jsch.JSchException;...