• shiro和redis集成,前后端分离


    前言

    框架:springboot+shiro+redis+vue
    最近写前后端分离授权的对账平台系统,采取了shiro框架,若采用shiro默认的cookie进行授权验证时,一直存在由于跨域造成前端请求到的cookie每次都不相同,从而无法完成授权及验证的操作,即每次登陆成功时还是会显示未登陆。

    Pom的引入

    <dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.0.0</version>
    <exclusions>
    <exclusion>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    </exclusion>
    </exclusions>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.4.1</version>
    </dependency>

    代码的编写

    public class CustomRealm extends AuthorizingRealm {
    
        @Resource
        private AdminDao adminDao;
    
        @Resource
        private PermissionDao permissionDao;
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            //获取登录认证成功的主体
            Admin admin = (Admin)principals.getPrimaryPrincipal();
    //        HashMap<String,String> admin = (HashMap)principals.getPrimaryPrincipal();
            //根据角色Id查询权限
            List<String> perms = new ArrayList<>();
    //        if (!admin.get("roleId").equals("-1")){
    //            perms = permissionDao.findPermsByRoles(admin.get("roleId"));
    //        }
            if (!admin.getRoleId().equals("-1")){
                perms = permissionDao.findPermsByRoles(admin.getRoleId());
            }
            //对集合中的字符串做过滤处理,去除 空字符串及null
            perms = perms.stream().filter(s->s != null && !s.isEmpty()).collect(Collectors.toList());
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addStringPermissions(perms);
            return info;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            //获取通过subject提交的主体信息
            String userName = (String)token.getPrincipal();
            Admin admin = adminDao.queryByUserName(userName);
            if(admin == null||admin.getFlag()==0){
                throw new UnknownAccountException("账号不存在或账号不可用,请联系管理员");
            }
    //        HashMap<String,String> map=new HashMap();
    //        map.put("username",admin.getUsername());
    //        map.put("roleId",admin.getRoleId());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(admin,admin.getPassword(),ByteSource.Util.bytes(admin.getSalt()),super.getName());
            return info;
        }
    }
    

    shiroConfig

    @Configuration
    public class ShiroConfig {
        @Value("${redis.port}")
        private Integer port;
    
        @Value("${redis.host}")
        private String host;
    
        /**
         * @author  zhuyang
         * @description 创建域,包括认证管理器,安全管理器
         * @date 2021-03-07 15:55
         * @param credentialsMatcher:  MD5加密器
         * @return com.yxkj.web.accountchecking.realm.CustomRealm
         */
        @Bean
        public CustomRealm customRealm(HashedCredentialsMatcher credentialsMatcher){
            CustomRealm customRealm = new CustomRealm();
            customRealm.setCredentialsMatcher(credentialsMatcher);
            return customRealm;
        }
        /**
         * @author  zhuyang
         * @description 安全管理器  shiro里面所有的权限控制,认证都事先通过他,安全管理器是和过滤器工厂打交道的桥梁
         * @date 2021-03-07 15:54
         * @param customRealm:
         * @return org.apache.shiro.web.mgt.DefaultWebSecurityManager
         */
        //只把记住我和域注入到安全管理器
        //如果记住我注入到域中,那么安全管理器还要和域打交道,这样就绕了
        @Bean
        public DefaultWebSecurityManager securityManager(CustomRealm customRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(customRealm);
            //添加开始
            CarWashModularRealmAuthorizer authorizer = new CarWashModularRealmAuthorizer();
            List<Realm> list=new ArrayList<>();
            list.add(customRealm);
            authorizer.setRealms(list);
            securityManager.setAuthorizer(authorizer);
            //将自定义的会话管理器注入到安全管理器中
            securityManager.setSessionManager(sessionManger());
            //将自定义的redis缓存管理器注册到安全管理器中
            securityManager.setCacheManager(cacheManager());
            //添加结束
            return securityManager;
        }
       /**
        * @author  zhuyang
        * @date 2021-03-07 15:57
        * @param securityManager: 先经过过滤器工厂,再通过subject提交token给DefaultWebSecurityManager(安全管理器)
        * @return org.apache.shiro.spring.web.ShiroFilterFactoryBean
        */
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
            ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
            Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
            //修改logout后的地址
            filtersMap.put("corsAuthenticationFilter", corsAuthenticationFilter());
            LogoutFilter logout = new LogoutFilter();
            logout.setRedirectUrl("/unauth");
            filtersMap.put("logout",logout);
            filterFactoryBean.setSecurityManager(securityManager);
            filterFactoryBean.setUnauthorizedUrl("/UnauthorizedUrl");
            Map map = new LinkedHashMap();
            map.put("/admin/getVerifyCode","anon");
            map.put("/admin/subLogin","anon");//访问登录
            map.put("/swagger-ui.html","anon");
            map.put("/admin/logout","logout");
            map.put("/doc.html","anon");
            map.put("/webjars/**","anon");
            map.put("/swagger-resources","anon");
            map.put("/freeQuery/manualCheck","anon");
            map.put("/v2/**","anon");
    //        map.put("/**","anon");
            map.put("/**","authc");
    
    
            filterFactoryBean.setFilters(filtersMap);
            filterFactoryBean.setFilterChainDefinitionMap(map);
            filterFactoryBean.setLoginUrl("/unauth");
            return filterFactoryBean;
        }
    
        @Bean
        public HashedCredentialsMatcher credentialsMatcher(){
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            credentialsMatcher.setHashAlgorithmName("md5");
            credentialsMatcher.setHashIterations(1024);
            return credentialsMatcher;
        }
    
    
        /**
         * @author  zhuyang
         * @description  如果您在使用shiro注解配置的同时,引入了spring aop的starter,
         * 会有一个奇怪的问题,导致shiro注解的请求,不能被映射,需加入以下配置
         * @date 2021-03-07 16:14
         * @return org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
         */
        @Bean
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            /**
             * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
             * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。
             * 加入这项配置能解决这个bug
             */
            defaultAdvisorAutoProxyCreator.setUsePrefix(true);
            return defaultAdvisorAutoProxyCreator;
        }
    
        /**
         * @author  zhuyang
         * @description 开启shiro注解的支持
         * @date 2021-03-07 16:20
         * @param securityManager:
         * @return org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
             AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
             advisor.setSecurityManager(securityManager);
             return advisor;
        }
    
        public CORSAuthenticationFilter corsAuthenticationFilter(){
            return new CORSAuthenticationFilter();
        }
    
    
    
    //    @Bean
    //    public SimpleCookie simpleCookie(){
    //        SimpleCookie cookie = new SimpleCookie();
    //        cookie.setName("rememberMe");
    //        cookie.setMaxAge(18);
    //        return cookie;
    //    }
    
    
    
    
        /**
         * @author  zhuyang
         * @description 1.redis的控制器,操作redis
         * @date 2021-03-07 14:41
         */
        public RedisManager redisManager(){
            RedisManager redisManager=new RedisManager();
            redisManager.setHost(host);
            redisManager.setPort(port);
            redisManager.setTimeout(2000);
            return redisManager;
        }
    
        /**
         * @author  zhuyang
         * @description sessiionDao
         * @date 2021-03-06 17:31
         */
        public RedisSessionDAO redisSessionDAO(){
            RedisSessionDAO sessionDAO=new RedisSessionDAO();
            sessionDAO.setRedisManager(redisManager());
    //        sessionDAO.setExpire(2000);
            return sessionDAO;
        }
        /**
         * @author  zhuyang
         * @description 会话管理器
         * @date 2021-03-06 17:30
         */
        public DefaultWebSessionManager sessionManger(){
            CustomSessionManager sessionManager=new CustomSessionManager();
            sessionManager.setSessionDAO(redisSessionDAO());
            sessionManager.setSessionIdCookieEnabled(false);
            sessionManager.setSessionIdUrlRewritingEnabled(false);
    //        sessionManager.setTimeout(new DefaultSessionKey(),100);
            return sessionManager;
        }
    
        /**
         * @author  zhuyang
         * @description  缓存管理器
         * @date 2021-03-06 17:31
         */
        public RedisCacheManager cacheManager(){
            RedisCacheManager redisCacheManager=new RedisCacheManager();
            redisCacheManager.setRedisManager(redisManager());
            return redisCacheManager;
        }
    }
    

    SessionId的获取

    public class CustomSessionManager extends DefaultWebSessionManager {
    
        @Override
        protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
            String id = WebUtils.toHttp(request).getHeader("token");
            //如果请求头中有 token 则其值为sessionId
            if (!StringUtils.isEmpty(id)) {
                //sessionId存放的位置
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
                //sesssionId的值
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
                //是否验证
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
                return id;
            } else {
                //否则按默认规则从cookie取sessionId
                return super.getSessionId(request, response);
            }
        }
    
    }
    

    解决403的问题

    public class CORSAuthenticationFilter extends FormAuthenticationFilter {
        public CORSAuthenticationFilter() {
            super();
        }
    
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
            if(request instanceof HttpServletRequest){
                if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")){
                    System.out.println("OPTIONS请求");
                    return true;
                }
            }
            return super.isAccessAllowed(request, response, mappedValue);
        }
    
    }
    

    授予管理员的一切权限,且不可修改

    public class CarWashModularRealmAuthorizer extends ModularRealmAuthorizer {
        @Override
        public  boolean isPermitted(PrincipalCollection principals, String permission){
    //        HashMap user = (HashMap) principals.getPrimaryPrincipal();
            Admin user = (Admin) principals.getPrimaryPrincipal();
            // 如果是管理员拥有所有的访问权限
            return user.getUsername().equals("gly") || super.isPermitted(principals, permission);
        }
        @Override
        public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
            Admin user = (Admin) principals.getPrimaryPrincipal();
            // 如果是管理员拥有所有的角色权限
            return user.getUsername().equals("gly") || super.hasRole(principals, roleIdentifier);
        }
    }
    

    异常拦截

    @ControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(AuthorizationException.class)
        @ResponseBody
        public Map handleAuthorizationException(AuthorizationException e){
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("code", "1000002");
            map.put("msg", "该用户没有对应的权限");
            return map;
        }
    
        @ExceptionHandler(Exception.class)
        @ResponseBody
        public Result exceptionHandler(Exception e){
            e.printStackTrace();
            log.error("异常日志打印"+e.getMessage());
            System.out.println(e.getClass().getName());
            return new Result(500,e.getMessage());
        }
    }
    

    其他处理

    @Controller
    @ApiIgnore
    public class ShiroController {
        @RequestMapping(value = "/unauth")
        @ResponseBody
        public Object unauth() {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("code", "1000000");
            map.put("msg", "未登录");
            return map;
        }
    
        @RequestMapping(value = "/UnauthorizedUrl")
        @ResponseBody
        public Object UnauthorizedUrl() {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("code", "1000001");
            map.put("msg", "未授权");
            return map;
        }
    }
    
    XFS
  • 相关阅读:
    面经二
    面经一
    Java集合-HashSet
    Java集合-LinkedList
    Java集合-ArrayList
    @JsonIgnoreProperties注解不起作用的问题解决
    纯JS实现图片验证码功能并兼容IE6-8
    java设计模式之桥接模式
    java设计模式之职责链模式
    WPF笔记:WPF自定义treeview样式及数据绑定
  • 原文地址:https://www.cnblogs.com/xiaofengshan/p/14586327.html
Copyright © 2020-2023  润新知