美文网首页
二、shiro(分布式前后端分离)自定义realm,cacheM

二、shiro(分布式前后端分离)自定义realm,cacheM

作者: LJasperliet | 来源:发表于2019-10-15 11:03 被阅读0次

    1.自定义realm

    自定义realm需要实现org.apache.shiro.realm.AuthorizingRealm


    image.png

    AuthorizingRealm 继承CachingRealm。
    在AuthenticatingRealm 源码中有这样一段代码


    image.png
    认证默认是不开启缓存的。

    而在AuthorizingRealm中


    image.png

    默认授权是开启缓存的

    自定义CustomRealm继承AuthorizingRealm

    package net.xdclass.rbac_shiro.config;
    
    import net.xdclass.rbac_shiro.domain.Permission;
    import net.xdclass.rbac_shiro.domain.Role;
    import net.xdclass.rbac_shiro.domain.User;
    import net.xdclass.rbac_shiro.service.UserService;
    import org.apache.ibatis.annotations.Param;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    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 java.util.ArrayList;
    import java.util.List;
    
    /**
    * 自定义realm
    */
    public class CustomRealm extends AuthorizingRealm {
    
       @Autowired
       private UserService userService;
    
       /**
        * 进行权限校验的时候回调用
        * @param principals
        * @return
        */
       @Override
       protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
           System.out.println("授权 doGetAuthorizationInfo");
    
           User newUser = (User)principals.getPrimaryPrincipal();
           User user = userService.findAllUserInfoByUsername(newUser.getUsername());
    
           List<String> stringRoleList = new ArrayList<>();
           List<String> stringPermissionList = new ArrayList<>();
    
    
           List<Role> roleList = user.getRoleList();
    
           for(Role role : roleList){
               stringRoleList.add(role.getName());
    
               List<Permission> permissionList = role.getPermissionList();
    
               for(Permission p: permissionList){
                   if(p!=null){
                       stringPermissionList.add(p.getName());
                   }
               }
    
           }
    
           SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
           simpleAuthorizationInfo.addRoles(stringRoleList);
           simpleAuthorizationInfo.addStringPermissions(stringPermissionList);
    
           return simpleAuthorizationInfo;
       }
       /**
        * 用户登录的时候会调用
        * @param token
        * @return
        * @throws AuthenticationException
        */
       @Override
       protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
           System.out.println("认证 doGetAuthenticationInfo");
    
           //从token获取用户信息,token代表用户输入
           String username = (String)token.getPrincipal();
    
           User user =  userService.findAllUserInfoByUsername(username);
           user.setId(null);
           //取密码
           String pwd = user.getPassword();
           if(pwd == null || "".equals(pwd)){
               return null;
           }
    
           return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
       }
    }
    
    

    config:

    /**
         * 自定义realm
         * @return
         */
        @Bean
        public CustomRealm customRealm(){
            CustomRealm customRealm = new CustomRealm();
    
            customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
            return customRealm;
        }
    
     /**
         * 密码加解密规则
         * @return
         */
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher(){
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    
            //设置散列算法:这里使用的MD5算法
            credentialsMatcher.setHashAlgorithmName("md5");
    
            //散列次数,好比散列2次,相当于md5(md5(xxxx))
            credentialsMatcher.setHashIterations(2);
    
            return credentialsMatcher;
        }
    

    2.自定义cacheManager

    默认情况下每次授权shiro都要查询数据库,这里我们使用redis实现授权信息的缓存功能。

    /**
         * 配置redisManager
         *
         */
        public RedisManager getRedisManager(){
            RedisManager redisManager = new RedisManager();
            redisManager.setHost("localhost");
            redisManager.setPort(6379);
            return redisManager;
        }
    
    
        /**
         * 配置具体cache实现类
         * @return
         */
        public RedisCacheManager cacheManager(){
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(getRedisManager());
    
            //设置过期时间,单位是秒,20s
            redisCacheManager.setExpire(50);
            redisCacheManager.setPrincipalIdFieldName("username");
            return redisCacheManager;
        }
    

    3.自定义sessionManager。单机下session没有什么问题,但是分布式情况下就需要维护session一致性。session存放到redis中,多个应用公用这一个session。

    session用法:用户每次请求,服务器后端都会生成一个sessionId。
    org.apache.shiro.web.session.mgt.DefaultWebSessionManager

    public Serializable getSessionId(SessionKey key) {
            Serializable id = super.getSessionId(key);
            if (id == null && WebUtils.isWeb(key)) {
                ServletRequest request = WebUtils.getRequest(key);
                ServletResponse response = WebUtils.getResponse(key);
                id = this.getSessionId(request, response);
            }
    
            return id;
        }
    

    上面方法生成id 会返回到前端


    image.png

    用户每次请求都会带入这个id,后台服务才能识别是同一个会话。

    前后端分离下,我们可以用这个sessionId作为token。登录后后台返回token,前端每次请求都在header中带上这个token作为会话。

    CustomSessionManager

    package net.xdclass.rbac_shiro.config;
    
    import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.apache.shiro.web.util.WebUtils;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.io.Serializable;
    
    public class CustomSessionManager extends DefaultWebSessionManager {
    
        private static final String AUTHORIZATION = "token";
    
    
        public CustomSessionManager(){
            super();
        }
    
        @Override
        protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
    
            String sessionId = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
    
            if(sessionId != null){
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                        ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
    
                //automatically mark it valid here.  If it is invalid, the
                //onUnknownSession method below will be invoked and we'll remove the attribute at that time.
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
                return sessionId;
    
            }else {
                return super.getSessionId(request,response);
            }
    
        }
    
    
    }
    
    

    config:

    /**
         * 自定义session持久化
         * @return
         */
        public RedisSessionDAO redisSessionDAO(){
            RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
            redisSessionDAO.setRedisManager(getRedisManager());
    
            //设置sessionid生成器
            //redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator());
    
            return redisSessionDAO;
        }
    
    //自定义sessionManager
        @Bean
        public SessionManager sessionManager(){
    
            CustomSessionManager customSessionManager = new CustomSessionManager();
    
            //超时时间,默认 30分钟,会话超时;方法里面的单位是毫秒
            //customSessionManager.setGlobalSessionTimeout(20000);
    
            //配置session持久化
            customSessionManager.setSessionDAO(redisSessionDAO());
    
            return customSessionManager;
        }
    

    最重要的

    @Bean
        public SecurityManager securityManager(){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    
            //如果不是前后端分离,则不必设置下面的sessionManager
            securityManager.setSessionManager(sessionManager());
    
            //使用自定义的cacheManager
            securityManager.setCacheManager(cacheManager());
    
            //设置realm(推荐放到最后,不然某些情况会不生效)
            securityManager.setRealm(customRealm());
            return securityManager;
        }
    

    完整config

    package net.xdclass.rbac_shiro.config;
    
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.mgt.SessionManager;
    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.crazycake.shiro.RedisCacheManager;
    import org.crazycake.shiro.RedisManager;
    import org.crazycake.shiro.RedisSessionDAO;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import javax.servlet.Filter;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    @Configuration
    public class ShiroConfig {
    
    
        @Bean
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
    
            System.out.println("执行 ShiroFilterFactoryBean.shiroFilter()");
    
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    
            //必须设置securityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            filterChainDefinitionMap.put("/pub/**","anon");
            filterChainDefinitionMap.put("/**", "authc");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
    
        @Bean
        public SecurityManager securityManager(){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    
            //如果不是前后端分离,则不必设置下面的sessionManager
            securityManager.setSessionManager(sessionManager());
    
            //使用自定义的cacheManager
            securityManager.setCacheManager(cacheManager());
    
            //设置realm(推荐放到最后,不然某些情况会不生效)
            securityManager.setRealm(customRealm());
    
    
    
            return securityManager;
        }
    
    
        /**
         * 自定义realm
         * @return
         */
        @Bean
        public CustomRealm customRealm(){
            CustomRealm customRealm = new CustomRealm();
    
            customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
            return customRealm;
        }
    
    
        /**
         * 密码加解密规则
         * @return
         */
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher(){
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    
            //设置散列算法:这里使用的MD5算法
            credentialsMatcher.setHashAlgorithmName("md5");
    
            //散列次数,好比散列2次,相当于md5(md5(xxxx))
            credentialsMatcher.setHashIterations(2);
    
            return credentialsMatcher;
        }
    
    
    
        //自定义sessionManager
        @Bean
        public SessionManager sessionManager(){
    
            CustomSessionManager customSessionManager = new CustomSessionManager();
    
            //超时时间,默认 30分钟,会话超时;方法里面的单位是毫秒
            //customSessionManager.setGlobalSessionTimeout(20000);
    
            //配置session持久化
            customSessionManager.setSessionDAO(redisSessionDAO());
    
            return customSessionManager;
        }
    
    
    
    
    
    
        /**
         * 配置redisManager
         *
         */
        public RedisManager getRedisManager(){
            RedisManager redisManager = new RedisManager();
            redisManager.setHost("localhost");
            redisManager.setPort(6379);
            return redisManager;
        }
    
    
        /**
         * 配置具体cache实现类
         * @return
         */
        public RedisCacheManager cacheManager(){
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(getRedisManager());
    
            //设置过期时间,单位是秒,20s
            redisCacheManager.setExpire(50);
            redisCacheManager.setPrincipalIdFieldName("username");
            return redisCacheManager;
        }
    
    
        /**
         * 自定义session持久化
         * @return
         */
        public RedisSessionDAO redisSessionDAO(){
            RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
            redisSessionDAO.setRedisManager(getRedisManager());
    
            //设置sessionid生成器
            //redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator());
    
            return redisSessionDAO;
        }
    
    
        /**
         * 管理shiro一些bean的生命周期 即bean初始化 与销毁
         * @return
         */
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
    
        /**
         *  api controller 层面
         *  加入注解的使用,不加入这个AOP注解不生效(shiro的注解 例如 @RequiresGuest)
         *
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
            return authorizationAttributeSourceAdvisor;
        }
    
    
        /**
         *  用来扫描上下文寻找所有的Advistor(通知器),
         *  将符合条件的Advisor应用到切入点的Bean中,需要在LifecycleBeanPostProcessor创建后才可以创建
         * @return
         */
        @Bean
        @DependsOn("lifecycleBeanPostProcessor")
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
            defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
            return defaultAdvisorAutoProxyCreator;
        }
    
    
    
    }
    
    

    相关文章

      网友评论

          本文标题:二、shiro(分布式前后端分离)自定义realm,cacheM

          本文链接:https://www.haomeiwen.com/subject/llujmctx.html