美文网首页
Apache Shiro+JWT

Apache Shiro+JWT

作者: 慵懒的阳光丶 | 来源:发表于2019-08-06 16:09 被阅读0次

    一、概述

    什么是Apache Shiro

    Apache Shiro是一个功能强大且灵活的开源安全框架,可以清晰地处理身份验证,授权,企业会话管理和加密。

    Apache Shiro的首要目标是易于使用和理解。安全有时可能非常复杂,甚至是痛苦的,但事实并非如此。框架应尽可能掩盖复杂性,并提供简洁直观的API,以简化开发人员确保其应用程序安全的工作。

    以下是Apache Shiro可以做的一些事情:

    验证用户以验证其身份
    为用户执行访问控制,例如:
    确定是否为用户分配了某个安全角色
    确定是否允许用户执行某些操作
    在任何环境中使用Session API,即使没有Web容器或EJB容器也是如此。
    在身份验证,访问控制或会话生命周期内对事件做出反应。
    聚合用户安全数据的1个或多个数据源,并将其全部显示为单个复合用户“视图”。
    启用单点登录(SSO)功能
    无需登录即可为用户关联启用“记住我”服务
    ......
    以及更多 - 全部集成到一个易于使用的内聚API中。
    Shiro尝试为所有应用程序环境实现这些目标 - 从最简单的命令行应用程序到最大的企业应用程序,而不会强制依赖其他第三方框架,容器或应用程序服务器。当然,该项目旨在尽可能地融入这些环境,但它可以在任何环境中开箱即用。

    Apache Shiro功能

    功能模块.png
    四大核心模块
    • 身份验证:有时称为“登录”,这是证明用户是他们所说的人的行为。

    • 授权:访问控制的过程,即确定“谁”可以访问“什么”。

    • 会话管理:即使在非Web或EJB应用程序中,也可以管理特定于用户的会话。

    • 密码学:使用加密算法保持数据安全,同时仍然易于使用。

    还有其他功能可以在不同的应用程序环境中支持和强化这些问题,尤其是:

    Web支持:Shiro的Web支持API可帮助轻松保护Web应用程序。
    缓存: 缓存是Apache Shiro API中的第一层公民,可确保安全操作保持快速高效。
    并发: Apache Shiro支持具有并发功能的多线程应用程序。
    测试: 存在测试支持以帮助您编写单元和集成测试,并确保您的代码按预期受到保护。
    “运行方式”: 允许用户假定其他用户的身份(如果允许)的功能,有时在管理方案中很有用。
    “记住我”: 记住用户在会话中的身份,因此他们只需要在必要时登录。

    二、架构分析

    1.Shiro的架构有3个主要概念:和SubjectSecurityManagerRealms
    架构图.png
    • Subject
      官网的描述为当前的用户、第三方服务等,其实就是与集成了shiro的系统交互的访客,抽象为Subject。
      Subject实例必须绑定一个SecurityManager
    • SecurityManager
      SecurityManager是Shiro架构的核心,充当一种“伞形”对象,协调其内部安全组件,共同形成对象图。 我们只需要对其进行相应的配置即可
      当我们与Subject交互时,实际上工作的是幕后的SecurityManager,它可以完成任何Subject安全操作的繁重任务。
    • Realms
      Realms充当Shiro与应用程序安全数据之间的“桥梁”或“连接器”。当实际与安全相关数据(如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个领域中查找许多这些内容。
      从这个意义上讲,Realm本质上是一个特定于安全性的DAO:它封装了数据源的连接细节,并根据需要使相关数据可用于Shiro。配置Shiro时,必须至少指定一个Realm用于身份验证和/或授权。所述SecurityManager可与多个境界被配置,但至少有一个是必需的。
      Shiro提供了开箱即用的Realms,可以连接到许多安全数据源(也称为目录),如LDAP,关系数据库(JDBC),文本配置源(如INI和属性文件等)。如果默认域不符合您的需要,您可以插入自己的Realm实现来表示自定义数据源。
      与其他内部组件一样,Shiro SecurityManager管理如何使用Realms获取要表示为Subject实例的安全性和身份数据。
    2.详细架构
    image.png
    • Subjectorg.apache.shiro.subject.Subject
      当前与软件交互的实体(用户,第三方服务,cron作业等)的特定于安全性的“视图”。

    • SecurityManagerorg.apache.shiro.mgt.SecurityManager
      如上所述,这SecurityManager是Shiro建筑的核心。它主要是一个“伞形”对象,协调其托管组件,以确保它们一起平稳运行。它还管理Shiro对每个应用程序用户的视图,因此它知道如何对每个用户执行安全操作。

    • 认证器org.apache.shiro.authc.Authenticator
      Authenticator是,负责执行和反应以验证(注册)用户企图的组件。当用户尝试登录时,该逻辑由执行Authenticator。该Authenticator知道如何与一个或多个协调Realms存储有关用户/帐户信息。从这些数据中获取的数据Realms用于验证用户的身份,以保证用户确实是他们所说的人。

    • AuthenticationStrategyorg.apache.shiro.authc.pam.AuthenticationStrategy
      如果Realm配置了多个,AuthenticationStrategy则将协调领域以确定身份验证尝试成功或失败的条件(例如,如果一个领域成功但其他领域失败尝试是否成功?必须所有领域成功吗?只有第一个?)。

    • Authorizerorg.apache.shiro.authz.Authorizer
      Authorizer是部件负责确定用户在该应用程序的访问控制。这种机制最终会说明是否允许用户做某事。与此类似Authenticator,它Authorizer也知道如何协调多个后端数据源以访问角色和权限信息。在Authorizer使用该信息来确定到底是否允许用户执行特定的操作。

    • SessionManagerorg.apache.shiro.session.mgt.SessionManager
      SessionManager知道如何创建和管理用户Session生命周期,提供在所有环境中的用户强大的会话体验。这是安全框架领域的一项独特功能 - 即使没有可用的Web / Servlet或EJB容器,Shiro也能够在任何环境中本地管理用户Sessions。默认情况下,Shiro将使用现有的会话机制(例如Servlet容器),但如果没有,例如在独立应用程序或非Web环境中,它将使用其内置的企业会话管理提供相同的编程经验。的SessionDAO存在允许任何数据源被用来坚持的会议。

    • SessionDAOorg.apache.shiro.session.mgt.eis.SessionDAO
      SessionDAO执行Session代表的持久性(CRUD)操作SessionManager。这允许将任何数据存储插入会话管理基础结构。

    • CacheManagerorg.apache.shiro.cache.CacheManager
      CacheManager创建和管理Cache其他四郎组件使用实例的生命周期。由于Shiro可以访问许多后端数据源以进行身份​​验证,授权和会话管理,因此缓存一直是框架中的一流架构功能,可在使用这些数据源时提高性能。任何现代开源和/或企业缓存产品都可以插入Shiro,以提供快速有效的用户体验。

    • Cryptographyorg.apache.shiro.crypto.*
      密码学是企业安全框架的自然补充。Shiro的crypto软件包包含易于使用和理解的密码密码,哈希(aka摘要)和不同编解码器实现的表示。该软件包中的所有类都经过精心设计,易于使用且易于理解。使用Java本机加密支持的任何人都知道它可能是一个具有挑战性的驯服动物。Shiro的加密API简化了复杂的Java机制,使密码学易于用于普通的凡人。

    • Realmorg.apache.shiro.realm.Realm
      如上所述,Realms充当Shiro与应用程序安全数据之间的“桥接”或“连接器”。当实际与安全相关数据(如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个领域中查找许多这些内容。您可以根据Realms需要配置任意数量(通常每个数据源一个),Shiro将根据需要进行身份验证和授权协调。

    总结:Subject相当于shiro的门面(前台),负责对外交互,实际的验证授权等需要由SecurityManager决定,而SecurityManager做出决定需要经过数据验证,那么数据由Realm来提供,到此就把shiro的框架流程串起来了。

    二、shiro整合spring

    由于我们在开发中,基本上都是结合spring使用shiro,所以这里略过了官网的入门案例

    Shiro一直支持Spring Web应用程序。在Web应用程序中,所有可通过Shiro访问的Web请求都必须通过主Shiro过滤器。此过滤器本身非常强大,允许基于任何URL路径表达式执行临时自定义过滤器链。
    以下是如何在基于Spring的Web应用程序中配置Shiro:

    1、采用xml方式配置(不推荐使用)

    在web.xml中配置shiro过滤器

    <!-- 过滤器的名称和你在applicationContext.xml中配置的bean注入必须一致 -->
    <!--见applicationContext.xml中 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    
    ...
    
    <!--在所有其他过滤器之前定义此过滤器,/ *捕获所有请求,确保过滤了您希望Shiro访问的任何请求。 -->
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    在applicationContext.xml中配置

    <!-- id必须和web.xml中的filter名称匹配-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!-- override these for application-specific URLs if you like:
       <!--指定跳转到的登录页面-->
        <property name="loginUrl" value="/login.jsp"/>
      <!--指定跳转到的成功页面-->
        <property name="successUrl" value="/home.jsp"/>
      <!--指定跳转到的无权限页面-->
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
        <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean  -->
        <!-- defined will be automatically acquired and available via its beanName in chain        -->
        <!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
        <!-- <property name="filters">
            <util:map>
                <entry key="anAlias" value-ref="someFilter"/>
            </util:map>
        </property> -->
    <!--根据需要添加过滤器到过滤器链中-->
        <property name="filterChainDefinitions">
            <value>
                # some example chain definitions:
                /admin/** = authc, roles[admin]
                /docs/** = authc, perms[document:read]
                /** = authc
                # more URL-to-FilterChain definitions here
            </value>
        </property>
    </bean>
    
    <!-- Define any javax.servlet.Filter beans you want anywhere in this application context.   -->
    <!-- They will automatically be acquired by the 'shiroFilter' bean above and made available -->
    <!-- to the 'filterChainDefinitions' property.  Or you can manually/explicitly add them     -->
    <!-- to the shiroFilter's 'filters' Map if desired. See its JavaDoc for more details.       -->
    <!--除了shiro提供的默认过滤器,也可以自定义过滤器-->
    <bean id="someFilter" class="..."/>
    <bean id="anotherFilter" class="..."> ... </bean>
    ...
    
    <!--配置securityManager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
        <property name="realm" ref="myRealm"/>
        <!-- By default the servlet container sessions will be used.  Uncomment this line
             to use shiro's native sessions (see the JavaDoc for more): -->
        <!-- <property name="sessionMode" value="native"/> -->
    </bean>
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
    <!--配置自定义的realm,用户用户信息的比对-->
    <!-- security datasource: -->
    <bean id="myRealm" class="...">
        ...
    </bean>
    

    开启shiro注解
    在请求接口中,也许我们会通过shiro的注解进行安全认证,(例如@RequiresRoles, @RequiresPermissions等等)
    方法很简单,我们只需要在applicationContext.xml中添加如下配置,但注意,此时必须保证lifecycleBeanPostProcessor进行了配置

    <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after -->
    <!-- the lifecycleBeanProcessor has run: -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
    

    2.采用注解的方式进行配置(推荐使用)

    shiro的配置

    2.1 首先自定义Realm
    /**
     * 认证
     *
     */
    @Component
    public class MyRealm extends AuthorizingRealm {
      
        /**
         * 授权(验证权限时调用)
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            User user = (User)principals.getPrimaryPrincipal();
            Long userId = user.getUserId();
    
            //用户权限列表
            Set<String> permsSet = shiroService.getUserPermissions(userId);
    
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.setStringPermissions(permsSet);
            return info;
        }
    
        /**
         * 认证(登录时调用)
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
         UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
            String username = usernamePasswordToken.getUsername();
           char[] password = usernamePasswordToken.getPassword();
          //根据用户名和密码去验证用户
            ...
          //验证通过后
            User user = getUser();
    
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password,username);
            return info;
        }
    }
    
    
    2.2 shiro的配置类
    /**
     * Shiro配置
     *
     */
    @Configuration
    public class ShiroConfig {
    
        @Bean("securityManager")
        public SecurityManager securityManager(MyRealm myRealm, SessionManager sessionManager) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myRealm);
            securityManager.setSessionManager(sessionManager);
    
            return securityManager;
        }
    
        @Bean("shiroFilter")
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
            shiroFilter.setSecurityManager(securityManager);
    
          //放行一些不用权限验证的路径
            Map<String, String> filterMap = new LinkedHashMap<>();
            filterMap.put("/webjars/**", "anon");
            filterMap.put("/druid/**", "anon");
            filterMap.put("/api/**", "anon");
            filterMap.put("/sys/login", "anon");
            filterMap.put("/**/*.css", "anon");
            filterMap.put("/**/*.js", "anon");
            filterMap.put("/**/*.html", "anon");
            filterMap.put("/img/**", "anon");
            filterMap.put("/fonts/**", "anon");
            filterMap.put("/plugins/**", "anon");
            filterMap.put("/swagger/**", "anon");
            filterMap.put("/favicon.ico", "anon");
            filterMap.put("/captcha.jpg", "anon");
            filterMap.put("/", "anon");
            filterMap.put("/**", "oauth2");
            shiroFilter.setFilterChainDefinitionMap(filterMap);
    
            return shiroFilter;
        }
      
        @Bean("lifecycleBeanPostProcessor")
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
    
    }
    

    配置过滤器,将上面写的过滤器加入到容器中

    package cn.environmental.config;
    
    import cn.expand.filter.CorsFilter;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.filter.DelegatingFilterProxy;
    
    import cn.environmental.common.xss.XssFilter;
    
    import javax.servlet.DispatcherType;
    
    /**
     * Filter配置
     *
     */
    @Configuration
    public class FilterConfig {
    
        @Bean
        public FilterRegistrationBean shiroFilterRegistration() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            //注意shiroFilter和shiroConfig中匹配
            registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
            //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
            registration.addInitParameter("targetFilterLifecycle", "true");
            registration.setEnabled(true);
            registration.setOrder(Integer.MAX_VALUE - 1);
            registration.addUrlPatterns("/*");
            return registration;
        }
    
      
    }
    

    三、Shiro整合JWT

    JWT的介绍不在这里进行说明了,可以自己查阅相关资料
    引入JWT后的过程如下:

    3.1 在用户登录的成功的时候为其生成token,并返回,用户访问其他接口时需要携带token!

    ①引入jjwt依赖

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>
    

    ②生成token

    public class JwtTest {
    
        /****
         * 创建Jwt令牌
         */
        @Test
        public void testCreateJwt(){
            JwtBuilder builder= Jwts.builder()
                    .setId("888")             //设置唯一编号
                    .setSubject("小白")       //设置主题  可以是JSON数据
                    //.addClaims(xxx)  自定义载荷信息
                    .setIssuedAt(new Date())  //设置签发日期
                    //.setExpiration(date)//用于设置过期时间 ,参数为Date类型数据
                    .signWith(SignatureAlgorithm.HS256,"秘钥");//设置签名 使用HS256算法,并设置SecretKey(字符串)
            //构建 并返回一个字符串
            System.out.println( builder.compact() );
    //eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NjIwNjIyODd9.RBLpZ79USMplQyfJCZFD2muHV_KLks7M1ZsjTu6Aez4
    
        }
    }
    

    ③解析token

    /***
     * 解析Jwt令牌数据
     */
    @Test
    public void testParseJwt(){
        String compactJwt="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NjIwNjI5MjUsImV4cCI6MTU2MjA2MjkyNX0._vs4METaPkCza52LuN0-2NGGWIIO7v51xt40DHY1U1Q";
        Claims claims = Jwts.parser().
                setSigningKey("秘钥").
                parseClaimsJws(compactJwt).
                getBody();
        System.out.println(claims);
    }
    
    3.2 引入token后需要在上面的shiroFilter中加入一个自定义的过滤器来专门验证token
    
    /**
     * token过滤器
     */
    public class OAuth2Filter extends AuthenticatingFilter {
    
        @Override
        protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
            //获取请求token
            String token = getRequestToken((HttpServletRequest) request);
    
            if(StringUtils.isBlank(token)){
                return null;
            }
    
            return new OAuth2Token(token);
        }
    
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
            if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){
                return true;
            }
    
            return false;
        }
    
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            //获取请求token,如果token不存在,直接返回401
            String token = getRequestToken((HttpServletRequest) request);
            if(StringUtils.isBlank(token)){
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
                httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
    
                String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));
    
                httpResponse.getWriter().print(json);
    
                return false;
            }
    
            return executeLogin(request, response);
        }
    
        @Override
        protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json;charset=utf-8");
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
            try {
                //处理登录失败的异常
                Throwable throwable = e.getCause() == null ? e : e.getCause();
                R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());
    
                String json = new Gson().toJson(r);
                httpResponse.getWriter().print(json);
            } catch (IOException e1) {
    
            }
    
            return false;
        }
    
        /**
         * 获取请求的token
         */
        private String getRequestToken(HttpServletRequest httpRequest){
            //从header中获取token
            String token = httpRequest.getHeader("token");
    
            //如果header中不存在token,则从参数中获取token
            if(StringUtils.isBlank(token)){
                token = httpRequest.getParameter("token");
            }
    
            return token;
        }
    
    
    }
    
    public class OAuth2Token implements AuthenticationToken {
        private String token;
    
        public OAuth2Token(String token){
            this.token = token;
        }
    
        @Override
        public String getPrincipal() {
            return token;
        }
    
        @Override
        public Object getCredentials() {
            return token;
        }
    }
    

    创建完验证token的过滤器后需要在shiroConfig中添加

    @Configuration
    public class ShiroConfig {
    
        @Bean("securityManager")
        public SecurityManager securityManager(OAuth2Realm oAuth2Realm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(oAuth2Realm);
            securityManager.setRememberMeManager(null);
            return securityManager;
        }
    
        @Bean("shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
            shiroFilter.setSecurityManager(securityManager);
    
            //token过滤验证
            Map<String, Filter> filters = new HashMap<>();
            filters.put("oauth2", new OAuth2Filter());
            shiroFilter.setFilters(filters);
    
            Map<String, String> filterMap = new LinkedHashMap<>();
            filterMap.put("/webjars/**", "anon");
            filterMap.put("/druid/**", "anon");
            filterMap.put("/app/**", "anon");
            filterMap.put("/sys/login", "anon");
            filterMap.put("/swagger/**", "anon");
            filterMap.put("/v2/api-docs", "anon");
            filterMap.put("/swagger-ui.html", "anon");
            filterMap.put("/swagger-resources/**", "anon");
            filterMap.put("/captcha.jpg", "anon");
            filterMap.put("/aaa.txt", "anon");
            filterMap.put("/**", "oauth2");
            shiroFilter.setFilterChainDefinitionMap(filterMap);
    
            return shiroFilter;
        }
    
        @Bean("lifecycleBeanPostProcessor")
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
    
    }
    

    token的过滤器的优先级比较高,这时候需要验证权限的接口就会先判断token是否有效了,需要将上面的Myrealm的认证方法改一下

      /**
         * 认证(登录时调用)
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            String accessToken = (String) token.getPrincipal();
    
            //验证token的代码(略)
              ...
            //token失效提醒用户(略)
            //return
           
            //查询用户信息
            User user = queryUser(user.getUserId());
    
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName());
            return info;
        }
    

    相关文章

      网友评论

          本文标题:Apache Shiro+JWT

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