SpringSecurity+Oauth2【授权码模式】+增强JWT实现单体应用资源访问控制

阅读本章节需要掌握springsecurity、oauth2协议、jwt


本次测试使用oauth2的授权码模式进行,和数据库交,互贴近实际生产。
  1. 新建spring工程。添加依赖
   
   
       4.0.0

   spring-boot-oauth-jwt-server
   spring-boot-oauth-jwt-server
   1.0-SNAPSHOT

   
       org.springframework.boot
       spring-boot-starter-parent
       2.1.3.RELEASE
   

   
       
           org.springframework.boot
           spring-boot-starter
       
       
           org.springframework.boot
           spring-boot-starter-web
       
       
           org.springframework.boot
           spring-boot-starter-data-jpa
       
       
           org.springframework.boot
           spring-boot-starter-security
       
       
           org.springframework.security.oauth
           spring-security-oauth2
           2.3.4.RELEASE
       
       
           org.springframework.security
           spring-security-jwt
           1.0.9.RELEASE
       
       
           org.springframework.boot
           spring-boot-starter-jdbc
       
       
           io.jsonwebtoken
           jjwt
           0.7.0
       
       
           mysql
           mysql-connector-java
       
       
           com.baomidou
           mybatis-plus-boot-starter
           3.3.2
       
       
           org.projectlombok
           lombok
           1.16.8
           provided
       
   

   
       
       
           
               
               
               
                   
               
           
       
       
           
               org.springframework.boot
               spring-boot-maven-plugin
               
                   com.buxiaoxia.Application
                   true
               
               
                   
                       
                           repackage
                       
                   
               
           
           
           
               maven-compiler-plugin
               
                   1.8
                   1.8
               
           
       
   

  1. 创建配置文件 Oauth2Config.java
package com.funtl.oauth2.server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class Oauth2Config {

    /**
     * 认证服务器
     */
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
        //注入自定义实现用户信息服务,网上大多是基于内存的操作,次实现表示从数据库中读取用户信息
        @Autowired
        private UserDetailsService userDetailsService;

        //数据源
        @Qualifier("dataSource")
        @Autowired
        private DataSource dataSource;

        //认证管理器authenticationManager 开启密码授权
        @Autowired
        private AuthenticationManager authenticationManager;

        //jwtToken增强,自定义实现token扩展
        @Autowired
        private TokenEnhancer jwtTokenEnhancer;

        //token存储,自定义实现保存到数据库中
        @Autowired
        private TokenStore tokenStore;

        //token转换器
        @Autowired
        private JwtAccessTokenConverter jwtAccessTokenConverter;

        //客户端信息,来源与DB
        @Bean
        public JdbcClientDetailsService clientDetailsService() {
            return new JdbcClientDetailsService(dataSource);
        }

        //自定义将自定授权保存数据库
        @Bean
        public ApprovalStore approvalStore() {
            return new JdbcApprovalStore(dataSource);
        }

        //将授权码保存数据库
        @Bean
        public AuthorizationCodeServices authorizationCodeServices() {
            return new JdbcAuthorizationCodeServices(dataSource);
        }

        //配置令牌端点(Token Endpoint)的安全约束.
        //授权服务安全配置,单体应用注意配置checkTokenAccess放行。
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            super.configure(security);
            //允许/oauth/token调用
            security.tokenKeyAccess("permitAll()")
                    //允许/oauth/check_token被调用
//                    .checkTokenAccess("permitAll()");
                    .checkTokenAccess("isAuthenticated()");
        }

         //配置客户端详情服务(ClientDetailsService)
         //客户端详情信息在这里进行初始化
         //通过数据库来存储调取详情信息
        //配置客户但详情从数据库读取,默认手动添加到oauth2客户端表中的数据
        @Bean
        public ClientDetailsService jdbcClientDetailes() {
            return new JdbcClientDetailsService(dataSource);
        }

        //客户但详情配置,基于JDBC
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

//        clients.inMemory()
//                .withClient("client")
//                .secret(passwordEncoder.encode("secret"))
//                .authorizedGrantTypes("authorization_code")
//                .scopes("app")
//                .redirectUris("https://www.baidu.com");
            clients.withClientDetails(jdbcClientDetailes());
        }

        /**
         *  配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
         *
         * @param endpoints
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET)
//                .authorizationCodeServices(authorizationCodeServices);

            endpoints.tokenStore(tokenStore)
                    .authorizationCodeServices(authorizationCodeServices())     //配置将授权码存放在oauth_code变中,默认存在内存中
                    .approvalStore(approvalStore())                             //配置审批存储oauth_approvals,存储用户审批过程,在一个月时间内不用再次审批
                    .authenticationManager(authenticationManager)
                    .userDetailsService(userDetailsService)
                    .reuseRefreshTokens(true);   //支持刷新令牌
            if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
                TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
                List enhancers = new ArrayList<>();
                enhancers.add(jwtAccessTokenConverter);
                enhancers.add(jwtTokenEnhancer);
                //将自定义Enhancer加入EnhancerChain的delegates数组中
                enhancerChain.setTokenEnhancers(enhancers);
                //为什么不直接把jwtTokenEnhancer加在这个位置呢?
                endpoints.tokenEnhancer(enhancerChain)
                        .accessTokenConverter(jwtAccessTokenConverter);
            }
        }
    }
    /**
     * 资源服务器
     */
    @EnableResourceServer
    public class ResourcesServerConfig extends ResourceServerConfigurerAdapter {
        @Autowired
        private DefaultTokenServices tokenServices;
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            //资源id可再次手动设置任意字符串,如果不设置,则需要在数据oauth_client_details中的resource_ids填写固定值"oauth2-resource"
            resources.resourceId("res1");
            resources.tokenServices(tokenServices);
            super.configure(resources);
        }

        //配置需要拦截的资源,这里可扩展的比较多,自由发挥
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/context")
                    .authorizeRequests()
                    .anyRequest()
                    .authenticated();
        }
    }
}

源码解读

资源服务器中配置的时候,需要注意有关资源id的配置:这里如果不配置,则在数据库中的oauth_client_details中的resource_ids填写固定值"oauth2-resource",如果配置,则需要和数据库中保持一致。

image.png

image.png

image.png

image.png
  1. 配置jwtTokenConfig
package com.funtl.oauth2.server.config;

import com.funtl.oauth2.jwt.MyJwtTokenEnhancer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

/**
 * @author zhan
 * @since 2019-12-13 15:28
 */
@Configuration
public class JwtTokenConfig {

    private static final String publicKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgN0Ka7Xv+2xgoUtuHtqBnKljMyBe5YZ/Zo7Jo1H9P0AwVDzPbmrBfq2Y2oqdFlcAUBs0UwKJ0FuqP6IgoRqCTBb5NmQo9nhgC0FGLF26wiLID1P0+lXoX02mhj6yqAGZDo3tMgk0xJ9pRybnqQOWJzAkISfI71by/IpOm5BZzzTNGH7sW8yxdw8K8+tFquKLMbKQcAAUa9/9l5VvIyvUci63Xt5URCWb6IDtwCNhu+cCs3ZBX6hcrdQW0VP46nG14+6fm50FpVEnQAXowfagP/ipdJcA/54sJeJ/m2vHQEbS4lKHhDUrfgIbJBaUmtk5ZkufVRkMSryjuIO1IasLbwIDAQAB-----END PUBLIC KEY-----";


    @Bean
//    @ConditionalOnBean(JwtTokenEnhancer.class)
    public TokenEnhancer jwtTokenEnhancer() {
        return new MyJwtTokenEnhancer();
    }

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
    
    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(jwtTokenStore());
        return defaultTokenServices;
    }
    /**
     * token生成处理:指定签名
     */

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("zhan.keystore"), "123456".toCharArray());
        //非对称加密
        accessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("zhan"));
//        accessTokenConverter.setVerifierKey(publicKey);
        //对称加密
//        accessTokenConverter.setSigningKey("123456");
        return accessTokenConverter;
    }
}

注意:这里需要强调一点的是必须向容器中注入DefaultTokenServices 的Bean,问什么呢,直接看源码就知道,这个类实现了认证的token和资源的token服务接口,在单体应用中它就用来生成和解析token

image.png

  1. 编写自定义token增强器
package com.funtl.oauth2.jwt;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;

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

/**
 *
 * token 增强器        Jwt token 扩展
 *
 * @author zhan
 * @since 2019-12-13 14:57
 */
public class MyJwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        String userName = authentication.getUserAuthentication().getName();
        // 与登录时候放进去的UserDetail实现类一直查看link{SecurityConfiguration}
        User user = (User) authentication.getUserAuthentication().getPrincipal();
        /** 自定义一些token属性 ***/
        final Map additionalInformation = new HashMap<>();
        additionalInformation.put("userName", userName);
        additionalInformation.put("address", "陕西省西安市");
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
//        OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
            return accessToken;







//        Map info = new HashMap<>(2);
//        info.put("username", "莹莹");
//        info.put("age", 26);
//        info.put("address", "陕西省西安市");
//        //设置附加信息
//        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
//        System.out.println(accessToken.getAdditionalInformation());
//        return accessToken;
    }
}

  1. 配置security的自定义配置
package com.funtl.oauth2.server.config;

import com.funtl.oauth2.server.config.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @author zhan
 * @since 2019-05-30 17:22
 */
@Configuration
@EnableWebSecurity
//开启方法级别支持spel表达式的角色权限校验注解,粒度更细,更好控制
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    /**
     * 需要配置这个支持password模式
     * support password grant type
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    @Bean
    public UserDetailsService userDetailsService(){
        return new UserDetailsServiceImpl();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置认证信息
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth
//                .inMemoryAuthentication()
//                .withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN")
//                .and()
//                .withUser("user").password(passwordEncoder().encode("123456")).roles("USER");
        auth.userDetailsService(userDetailsService());
    }
}

其中比较重要的几个类:OAuth2AuthenticationManager认证管理,OAuth2AuthenticationProcessingFilter 核心过滤器
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
            ServletException {

        final boolean debug = logger.isDebugEnabled();
        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;

        try {

            Authentication authentication = tokenExtractor.extract(request);
            
            if (authentication == null) {
                if (stateless && isAuthenticated()) {
                    if (debug) {
                        logger.debug("Clearing security context.");
                    }
                    SecurityContextHolder.clearContext();
                }
                if (debug) {
                    logger.debug("No token in request, will continue chain.");
                }
            }
            else {
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
                if (authentication instanceof AbstractAuthenticationToken) {
                    AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
                    needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
                }
                Authentication authResult = authenticationManager.authenticate(authentication);

                if (debug) {
                    logger.debug("Authentication success: " + authResult);
                }

                eventPublisher.publishAuthenticationSuccess(authResult);
                SecurityContextHolder.getContext().setAuthentication(authResult);

            }
        }
        catch (OAuth2Exception failed) {
            SecurityContextHolder.clearContext();

            if (debug) {
                logger.debug("Authentication request failed: " + failed);
            }
            eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
                    new PreAuthenticatedAuthenticationToken("access-token", "N/A"));

            authenticationEntryPoint.commence(request, response,
                    new InsufficientAuthenticationException(failed.getMessage(), failed));

            return;
        }

        chain.doFilter(request, response);
    }

认证流程


授权流程


image.png

授权决策【这就是传说中的投票器机制】

AccessDecisionManager的投票机制
|
AffirmativeBased一票通过
UnanimousBased一票反对
ConsensusBased少数服从多数
AccessDecisionVoter访问决策投票器
RoleVoter角色投票
AuthenticatedVoter认证投票
有兴趣可以去看看源码,非常有意思!

附上本次testing笔记

https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=101218304&redirect_uri=http://passport.itheima.com/connect/qq&state=b78bbdf538b7369ce3cf5169d506faab&scope=

http://localhost:8080/logout            sprign security 登出


// 获取授权码
http://localhost:8080/oauth/authorize?response_type=code&client_id=test&redirect_uri=https://www.baidu.com&scope=all&state=hello

http://localhost:8081/oauth/authorize?client_id=client&response_type=code

http://localhost:13000/oauth/authorize?client_id=client&response_type=code

http://localhost:8080/oauth/check_token?token=

// 获取token
http://localhost:8080/oauth/token?grant_type=authorization_code&code=XozVUL&client_id=test&redirect_uri=https://www.baidu.com/&scpoe=all



\A\$2a?\$\d\d\$[./0-9A-Za-z]{53}        OAuth2.0 密码正则



http.antMatcher("/").authorizeRequests();
大体意思就是antMatcher()``是HttpSecurity的一个方法,他只告诉了Spring我只配置了一个我这个Adapter能处理哪个的url,它与authorizeRequests()没有任何关系。

http.authorizeRequests().antMatchers("/").hasRole("");
然后使用authorizeRequests().antMatchers()是告诉你在antMatchers()中指定的一个或多个路径,比如执行permitAll()或hasRole()。他们在第一个http.antMatcher()匹配时就会生效。

========================================================================================================

资源服务 (包含公钥)
认证服务  (包含私钥)
私钥用于springsecurity生成JWT令牌,公钥放于资源服务。用于访问资源

keytool ‐list ‐rfc ‐‐keystore xc.keystore | openssl x509 ‐inform pem ‐pubkey

keytool -list -v -keystore kevin_key.jks -storepass 123456

keytool ‐list ‐rfc ‐‐keystore kevin_key.jks | openssl x509 ‐inform pem ‐pubkey

Keytool是一个java提供得证书管理工具
    --alias:    密钥别名
    --keyalg:   使用得hash算法
    --keypass:  密钥访问得密码
    --keystore: 密钥库文件名   zhan.keystore保存了生成得证书
    --storepass:密钥库得访问密码

1、生成密钥证书  【RSA算法每个证书都包含公钥和私钥,私钥生成得令牌公钥可以去校验】

keytool -genkeypair -alias kevin_key -keyalg RSA -keypass 123456 -keystore kevin_key.jks -storepass 123456

keytool -genkeypair -alias zhan -keyalg RSA -keypass 123456 -keystore zhan.keystore -storepass 123456



2.查看证书信息:
keytool -list -v -keystore zhan.keystore -storepass 123456

keytool -list -keystore zhan.keystore

3.查看公钥信息

keytool -list -rfc --keystore zhan.keystore | openssl x509 -inform pem -pubkey


-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgN0Ka7Xv+2xgoUtuHtqBnKljMyBe5YZ/Zo7Jo1H9P0AwVDzPbmrBfq2Y2oqdFlcAUBs0UwKJ0FuqP6IgoRqCTBb5NmQo9nhgC0FGLF26wiLID1P0+lXoX02mhj6yqAGZDo3tMgk0xJ9pRybnqQOWJzAkISfI71by/IpOm5BZzzTNGH7sW8yxdw8K8+tFquKLMbKQcAAUa9/9l5VvIyvUci63Xt5URCWb6IDtwCNhu+cCs3ZBX6hcrdQW0VP46nG14+6fm50FpVEnQAXowfagP/ipdJcA/54sJeJ/m2vHQEbS4lKHhDUrfgIbJBaUmtk5ZkufVRkMSryjuIO1IasLbwIDAQAB-----END PUBLIC KEY-----

-------------------------------------------------------------------------------------------------
-------------------------------request header--------------------------------------------------------------------------------------------------------------
request报文

  



----------------------------------------

GET /login/code/image HTTP/1.1
Host: 133.64.135.164:8000
Connection: keep-alive
Authorization: Basic dGVzdDp0ZXN0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36
Accept: */*
Referer: http://133.64.135.164:8000/user/login
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7

-------------------------------response header------------------------------------------------------------------------------------------------------------
response报文

  



-----------------------------------------

HTTP/1.1 200 OK
X-Powered-By: Express
imagecodesession: 99a126fb-e962-441f-a88e-c69913375278
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-frame-options: DENY
transfer-encoding: chunked
date: Mon, 30 Dec 2019 07:27:37 GMT
connection: close



{
  "singleServerConfig":{
    "address": "redis://127.0.0.1:6379",
    "password": null,
    "database": 1
  },
  "codec":{
    "class":"org.redisson.codec.FstCodec"
  },
  "transportMode":"NIO"
}









Some systems may allow for approval decisions to be remembered or approved by default. Check for
such logic here, and set the approved flag on the authorization request accordingly.

有些系统可能默认允许记住或批准审批决策。检查

这样的逻辑,并相应地在授权请求上设置approved标志。
------------------------
Place auth request into the model so that it is stored in the session
for approveOrDeny to use. That way we make sure that auth request comes from the session,
so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.

将身份验证请求放置到模型中,以便将其存储在会话中

申请批准或拒绝使用。这样我们可以确保auth请求来自会话,

因此,传递给approveOrDeny的任何身份验证请求参数都将被忽略并从会话中检索。

--------------------------------
[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7945b206, org.springframework.security.web.context.SecurityContextPersistenceFilter@180b3819, org.springframework.security.web.header.HeaderWriterFilter@6f8e9d06, 
org.springframework.security.web.csrf.CsrfFilter@6397248c, org.springframework.security.web.authentication.logout.LogoutFilter@3becc950, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@83bb0f, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@39666e42, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@7d42542, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@47272cd3, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5e26f1ed, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4d09cade, org.springframework.security.web.session.SessionManagementFilter@2272cbb0, org.springframework.security.web.access.ExceptionTranslationFilter@53f0d09c, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@1da1380b]

微服务一般授权和资源服务器分开,这时候我们的思路是,在网关处同意拦截请求,验证token,符合直接将token发送给各个微服务,由各个问服务自己解析token

你可能感兴趣的:(SpringSecurity+Oauth2【授权码模式】+增强JWT实现单体应用资源访问控制)