• 踩坑篇-Nacos+Sping-gateway+shiro实现分布式认证权限框架


    踩坑篇-Nacos+Sping-gateway+shiro实现分布式认证权限框架

    创作背景

    我们公司属于通信行业,做出的产品用户群体比较小众,所以不太追求像互联网公司那样的Tps,Qps之类的指标,单体springboot其实已经够用了。但是身为技术人嘛,总要有所追求。
    另外一个原因,也是最重要的原因:shiro很少被用来做为微服务的安全框架,这让我替这个非常简单易用的框架鸣不,平开发这个,也是来证明微服务安全框架并不是只能选spring security。另外微服务中用shiro来实现安全框架的文章几乎,这对广大用户shiro很不友好。

    基本功能

    1. Nacos作为注册中心和配置中心(包括logback配置)
    2. spring-gateway作为网关,具备熔断,负载,限流,统一操作日志,认证权限拦截等功能
    3. shiro+redis作为认证授权服务 oaa,提供为网关feign接口,用来验证权限

    版本说明

    网上贴的很多稳定版本,大家也可以自由选取,版本不要太低,不方便以后拓展。一下是我实践的一套,目前这套依赖完美契合:
    nacos 1.1.4(网传最稳定版本);
    springcloud Hoxton.SR8
    springcloud alibaba 2.2.2.RELEASE
    springboot 2.3.6.RELEASE

    认证中心实现篇

    1.登录类

    package com.eastcom.oaa.controller;
    
    import com.alibaba.fastjson.JSON;
    import common.common.exception.ExceptionCast;
    import common.common.util.GetRealIPUtil;
    import common.entity.*;
    import common.pojo.User;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.subject.Subject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.web.bind.annotation.*;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @description: 登陆
     */
    
    @CrossOrigin
    @RestController
    @RequestMapping("/login")
    public class LoginController {
    
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
    
    
        /**
         * @param user_name
         * @param Pwd
         * @return
         * @throws Exception
         */
        @PostMapping("/loginByPwd")
        public Result loginPCByPwd(@RequestParam String user_name, @RequestParam String Pwd, HttpServletRequest request) {
    
            // 认证 Subject:主体
            Subject subject = SecurityUtils.getSubject();
            // 根据用户信息,组成用户令牌token
            UsernamePasswordToken Token = new UsernamePasswordToken(user_name, Pwd, false);
            subject.login(Token);
            User user1 = (User) subject.getPrincipal();
            QueryResult queryResult = new QueryResult();
            String token = subject.getSession().getId().toString();
            queryResult.setData(user1)
            queryResult.setToken(token)
            return new Result(CommonCode.LOG_OUT_SUCCESS,queryResult);
        }
    
        /**
         * @Author mazl
         * @Description:
         * @Return
         */
        @RequestMapping("/logout")
        public Result logout(@RequestParam String user_name){
            SecurityUtils.getSubject().logout();
           
            QueryResult queryResult = new QueryResult();
            return new Result(CommonCode.LOG_OUT_SUCCESS,queryResult);
        }
    
        @GetMapping("/isPermitted")
        public boolean isPermitted(@RequestParam String requestURI,@RequestParam String token){
            System.out.println("isPermitted");
            logger.info("进入授权,访问路径:{}",requestURI);
    //        //方案一,不灵活(对于get请求,不允许在url通过/拼接参数,可以通过?拼接)、不易排查问题
            boolean permitted =SecurityUtils.getSubject().isPermitted(requestURI);
            System.out.println("是否授权:"+permitted);
            return permitted;
            //方案二,不灵活,且无权限时报的异常无法捕获
    //        subject.checkPermissions(requestURI);
    
            //方案一,过于灵活
    //      User parse = (User) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
    //        List resources = parse.getResources();
    //        if("admin".equals(parse.getRole_name())){
    //            System.out.println("放行管理员");
    //            return true;
    //
    //        }else
    //            if(requestURI.endsWith(".js") || requestURI.endsWith(".css")|| requestURI.endsWith(".gif")){
    //            System.out.println("放行资源路径");
    //            return true;
    //        }else {
    //            //开关
    //            boolean flag = false;
    //            for(String resource:resources){ //判断当前访问的 URI 是否在功能数据中包含
    //                if(requestURI.indexOf(resource) !=-1){
    //                    flag = true;
    //                    System.out.println(requestURI+"--->"+resource);
    //                    break;
    //                }
    //            }
    //            return flag;
    //        }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    上面登录登出很一般的代码,最后一个要说一下,结合shiro给网关提供的权限接口,具体有三种判断权限的方式,用哪种可以根据需求自由选取。

    2.shirorealm 玩shiro的不用我说了吧

    package com.eastcom.oaa.config;
    
    
    import com.eastcom.oaa.mapper.RmAuResourceMapper;
    import com.eastcom.oaa.service.Shiro.ShiroService;
    import com.eastcom.oaa.service.Shiro.impl.ShiroServiceImpl;
    import common.common.exception.ExceptionCast;
    import common.entity.CommonCode;
    import common.pojo.User;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.SimplePrincipalCollection;
    import org.apache.shiro.subject.support.DefaultSubjectContext;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.Collection;
    import java.util.List;
    
    
    public class ShiroRealm extends AuthorizingRealm{
    
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    
        @Autowired
        public  ShiroService shiroService;
        @Autowired
        public  RmAuResourceMapper rmAuResourceMapper;
    
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken){
            System.out.println("进入shiroRealm");
            UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
            // 从数据库获取对应用户名密码的用户
            System.out.println(token.toString());
            System.out.println(token.getUsername());
            User user = shiroService.selecUserByAccount(token.getUsername());
            if (user != null) {
                if(user.getUser_state()==2){
                    ExceptionCast.shiroCast(CommonCode.FORBIDDEN,"ShiroRealm","doGetAuthenticationInfo");
                }
    
                user.setResources(shiroService.getResourceUrlByUserName(user.getUser_name()));
                this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
    
                //单用户登录
                //处理session
                DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
                SessionManager sessionManager = (SessionManager) securityManager.getSessionManager();
                //获取当前已登录的用户session列表
                Collection sessions = sessionManager.getSessionDAO().getActiveSessions();
                User temp;
                for(Session session : sessions) {
    
                    Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                    if (attribute == null) {
                        continue;
                    }
    
                    temp = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
                    logger.info("当前已登录的用户user: "+temp.toString());
                    //清除该用户以前登录时保存的session,强制退出
                    if (token.getUsername().equals(temp.getUser_name())) {
                        sessionManager.getSessionDAO().delete(session);
                    }
                }
    
                SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                        user, //用户
                        user.getUser_pasw(), //密码
                        getName()  //realm name
                );
                return authenticationInfo;
            }else {
                ExceptionCast.shiroCast(CommonCode.NULL_USER,"ShiroRealm","doGetAuthenticationInfo");
            }
            throw new AuthenticationException();
        }
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
    //        // TODO Auto-generated method stub
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //获取登录时查询到的用户对象
            User user = (User)arg0.getPrimaryPrincipal();
            //把用户的所有权限添加到info中
            logger.info("当前正在授权的用户信息: "+user.toString());
            List resources = user.getResources();
            if(resources!=null&&resources.size()>0){
                for(String URL : user.getResources()){
                    info.addStringPermission(URL);
                }
            }
           //后台只需要做登录认证即可,权限部分由前端动态遍历授权页面即可
    
            return info;
        }
    
        /**
         * 管理员授权
         * @param principals
         * @param permission
         * @return
         */
        @Override
        public  boolean isPermitted(PrincipalCollection principals, String permission){
            User user = (User)principals.getPrimaryPrincipal();
            return isAdmin(user)||super.isPermitted(principals,permission);
        }
        @Override
        public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
            User user = (User)principals.getPrimaryPrincipal();
            return isAdmin(user)||super.hasRole(principals,roleIdentifier);
        }
    
        public boolean isAdmin( User user) {
            return "admin".equals(user.getRole_name());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128

    3.shiroConfig 同上,玩过单体springboot+shiro的这套应该不难,由于认证和权限放在网关做,所以此处拦截路径完全放开

    package com.eastcom.oaa.config;
    
    
    import org.apache.shiro.mgt.SecurityManager;
    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.servlet.SimpleCookie;
    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.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    @Configuration
    @CrossOrigin
    public class ShiroConfigOaa {
    
        /**
         * 注入配置文件属性
         */
        @Value("${spring.redis.host}")
        private String host;//地址
        @Value("${spring.redis.port}")
        private int port;
        @Value("${spring.redis.timeout}")
        private int timeout;//过期时间
        @Value("${spring.redis.password}")
        private String password;//地址
        //将自己的验证方式加入容器
        @Bean
        public ShiroRealm ShiroRealm() {
    
            ShiroRealm myShiroRealm = new ShiroRealm();
            myShiroRealm.setCredentialsMatcher(credentialsMatcher());
            return myShiroRealm;
        }
        @Bean
        public MyCredentialsMatcher credentialsMatcher() {
    
            return new MyCredentialsMatcher();
        }
        //Filter工厂,设置对应的过滤条件和跳转条件
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            return shiroFilterFactoryBean;
    
        }
    
    
    
        //权限管理,配置主要是Realm的管理认证
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(ShiroRealm());
            // 自定义缓存实现 使用redis
            securityManager.setCacheManager(cacheManager());
            // 自定义session管理 使用redis
            securityManager.setSessionManager(sessionManager());
            return securityManager;
        }
    
        /**
         * cacheManager 缓存 redis实现
         * 使用的是shiro-redis开源插件
         *
         * @return
         */
        @Bean
        public RedisCacheManager cacheManager() {
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(redisManager());
            redisCacheManager.setKeyPrefix("SPRINGBOOT_CACHE:");   //设置前缀
            return redisCacheManager;
        }
    
        /**
         * Session User
         * 使用的是shiro-redis开源插件
         */
        @Bean
        public SessionManager sessionManager() {
            SimpleCookie simpleCookie = new SimpleCookie("token");
            simpleCookie.setPath("/");
            simpleCookie.setHttpOnly(false);
    
            SessionManager sessionManager = new SessionManager();
            sessionManager.setSessionDAO(redisSessionDAO());
            sessionManager.setSessionIdCookieEnabled(false);
            sessionManager.setSessionIdUrlRewritingEnabled(false);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionIdCookie(simpleCookie);
            sessionManager.setGlobalSessionTimeout(3600 * 1000);//单位毫秒
            return sessionManager;
        }
    
        /**
         * 配置shiro redisManager
         * 使用的是shiro-redis开源插件
         *
         * @return
         */
        @Bean
        public RedisManager redisManager() {
            RedisManager redisManager = new RedisManager();
            redisManager.setHost(host);
            redisManager.setJedisPool(redisPoolFactory());
            redisManager.setPassword(password);
            redisManager.setTimeout(timeout);
            return redisManager;
        }
        @Bean
        public JedisPool redisPoolFactory() {
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
    
            return jedisPool;
        }
    
    
        /**
         * RedisSessionDAO shiro sessionDao层的实现 通过redis
         * 使用的是shiro-redis开源插件
         */
        @Bean
        public RedisSessionDAO redisSessionDAO() {
            RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
            redisSessionDAO.setRedisManager(redisManager());
            redisSessionDAO.setKeyPrefix("SPRINGBOOT_SESSION:");
            redisSessionDAO.setExpire(3600);//设置过期时间,单位s
            return redisSessionDAO;
        }
    
        /***
         * 授权所用配置
         *
         * @return
         */
        @Bean
        public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
            return defaultAdvisorAutoProxyCreator;
        }
        //加入注解的使用,不加入这个注解不生效
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
        /**
         * Shiro生命周期处理器
         * 此方法需要用static作为修饰词,否则无法通过@Value()注解的方式获取配置文件的值
         *
         */
        @Bean
        public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173

    4.sessionManager的实现。

    package com.eastcom.oaa.config;
    
    import common.entity.UtilCode;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.UnknownSessionException;
    import org.apache.shiro.session.mgt.SessionKey;
    import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.apache.shiro.web.session.mgt.WebSessionKey;
    import org.apache.shiro.web.util.WebUtils;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.io.Serializable;
    
    public class SessionManager  extends DefaultWebSessionManager {
        private static final String AUTHORIZATION = UtilCode.TOKEN;
    
        private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    
        public SessionManager() {
        }
    
        @Override
        protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
            //获取请求头,或者请求参数中的token
            String baseid = StringUtils.isEmpty(WebUtils.toHttp(request).getHeader(AUTHORIZATION))
                    ? request.getParameter(AUTHORIZATION) : WebUtils.toHttp(request).getHeader(AUTHORIZATION);
            // 如果请求头中有 Token 则其值为sessionId
    
            if (!StringUtils.isEmpty (baseid)) {
    
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, baseid);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
    
                return baseid;
            } else {
                // 否则按默认规则从cookie取sessionId
                return super.getSessionId(request, response);
            }
        }
    
        /**
         * 获取session 优化单次请求需要多次访问redis的问题
         *
         * @param sessionKey
         * @return
         * @throws UnknownSessionException
         */
        @Override
        protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
            Serializable sessionId = getSessionId(sessionKey);
    
            ServletRequest request = null;
            if (sessionKey instanceof WebSessionKey) {
                request = ((WebSessionKey) sessionKey).getServletRequest();
            }
    
            if (request != null && null != sessionId) {
                Object sessionObj = request.getAttribute(sessionId.toString());
                if (sessionObj != null) {
                    return (Session) sessionObj;
                }
            }
    
            Session session = super.retrieveSession(sessionKey);
            if (request != null && null != sessionId) {
                request.setAttribute(sessionId.toString(), session);
            }
            return session;
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    其实微服务整合shiro核心地方到这已经讲完了,不知道你留意到了没~

    网关实现篇

    1.实现认证中心的授权接口的feign调用

    package com.eastcom.springgateway.FeignService;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @FeignClient(name="oaa",fallbackFactory = GatewayFeignFallbackFactory.class)
    public interface OaaFeign {
        @RequestMapping(method = RequestMethod.GET, value = "/login/isPermitted")
        boolean isPermitted(@RequestParam String requestURI,@RequestParam String token);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.接下来就没什么难的了,实现GlobalFilter, Ordered来自定义过滤,考虑到代码涉密,不贴了。

    效果呈现

    登录并获取token
    登录并获取token
    调用oaa服务接口,oaa通过feign调用server服务接口
    在这里插入图片描述
    在这里插入图片描述

    用错误token进行上一步操作
    在这里插入图片描述
    在这里插入图片描述

    完工

    简单的解释

    由于做的是政企项目,这套架构可能会在以后投入使用。政企项目上线前都会安全检查的,包括代码联网审查(github,oschina等,csdn我不清楚有没有),所以保险起见,对上面的登录登出代码有删减,其他地方也没有贴完,望大家见谅。有问题可以评论区留言,谢谢大家

  • 相关阅读:
    springboot(ssm 企业员工薪酬关系系统 Java(code&LW)
    MySQL高级12-事务原理
    ARM 汇编指令作业(求公约数、for循环实现1-100之间和、从SVC模式切换到user模式简单写法)
    Windows Server 2016 Standard 激活,亲测有效
    2022-11-17 mysql列存储引擎-聚合运算中间结果缓存磁盘文件以避免OOM-需求分析
    【WebLogic】Oracle发布2022年第三季度中间件安全公告
    你可能从未使用过的13个Python特性
    边缘计算AI智能安防监控视频平台车辆违停算法详解与应用
    项目架构的发展
    【Redis】CentOs 虚拟机安装 Redis 缓存数据库
  • 原文地址:https://blog.csdn.net/m0_67391521/article/details/126617660