美文网首页SpringSecurityOAuth2
SpringSecurity+JWT配置的坑以及源码分析

SpringSecurity+JWT配置的坑以及源码分析

作者: KingdomCoder | 来源:发表于2019-01-30 18:03 被阅读0次

    抛出问题:

    之前在QQ上有位朋友问jwt的问题,开始的时候只给了他一个自己写的参考案例,以为整个过程就可以顺利配置并且可以愉快的使用,但是后面遇到了一个小坑,可能平时配置自定义AuthorizationServerConfigurerAdapter的自定义父类的时候,一下子就入坑了,好现在先抛出代码然后分析,以下为部分核心配置代码:

    @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager);
            //通过TokenEnhancerChain增强器链将jwtAccessTokenConverter(转换成jwt)和jwtTokenEnhancer(往里面加内容加信息)连起来
            TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = Lists.newArrayList();
            enhancerList.add(tokenEnhancer());
            enhancerList.add(accessTokenConverter());
            enhancerChain.setTokenEnhancers(enhancerList);
            endpoints
                .tokenEnhancer(enhancerChain)
                .tokenServices(tokenServices())
                .accessTokenConverter(accessTokenConverter());
    
        }
    
        @Primary
        @Bean
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
            defaultTokenServices.setTokenStore(tokenStore());
            //维持刷新token
            defaultTokenServices.setSupportRefreshToken(true);
            return defaultTokenServices;
        }
    
       @Bean
        public TokenStore tokenStore() {
            TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
            return tokenStore;
        }
    
        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
            accessTokenConverter.setSigningKey(jwtSigningKey);
            return accessTokenConverter;
        }
    
        @Bean
        public TokenEnhancer tokenEnhancer() {
            return new CustomTokenEnhancer();
        }
    

    可是启动执行获取token接口返回值为:

    {
        "access_token": "35cdbd07-9b5e-40ab-b69f-8eacaa2c1fc7",
        "token_type": "bearer",
        "refresh_token": "db5aa1e5-512b-45a4-9d55-aa137d710f67",
        "expires_in": 43199,
        "scope": "read,write"
    }
    

    乍一看这个没啥问题,我明明配置的是jwt的accessTokenConvertertokenStore可是启动程序生成的token怎么不是jwt呢,而是uuid的一串字符串,自己也没发现有啥问题,找呀找呀,时间就这么过去了......

    源码分析:

    通过跟踪源码分析TokenEndpoint的接口方法postAccessToken可以发现生成token的执行的方法为:

        OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
    

    其中其核心生成token的类为DefaultTokenServices执行的方法为createAccessToken可以看到源码中生成token的方法为createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken)的私有方法,源码为:

    private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
            DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
            int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
            if (validitySeconds > 0) {
                token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
            }
            token.setRefreshToken(refreshToken);
            token.setScope(authentication.getOAuth2Request().getScope());
            //这个才是自定义token生成策略的关键
            return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
        }
    

    可以发现这边在createToken的时候首先是使用UUID来生成的,然后在最后一行
    accessTokenEnhancer != null采用的是accessTokenEnhancer配置的token生成策略,若为null则直接返回生成的uuidtoken.

    失效原因分析:

    源码的token生成策略我们已经分析完成,现在让我们回过头来看看我们之前的配置代码TokenEnhancerChain已经设置了我们的tokenStoreaccessTokenConverter但是为啥没生效呢......?
    可以看到我们在重写configure(AuthorizationServerEndpointsConfigurer endpoints)这个方法的时候执行了一个命令endpoints. .tokenServices(tokenServices())tokenService()方法我们配置的是什么呢?

    @Primary
        @Bean
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
            defaultTokenServices.setTokenStore(tokenStore());
            //维持刷新token
            defaultTokenServices.setSupportRefreshToken(true);
            return defaultTokenServices;
        }
    

    我们这边重new DefaultTokenServices(),并且没有配置DefaultTokenServices下的private TokenEnhancer accessTokenEnhancer;这个属性,所以导致我们在DefaultTokenServices类下面执行createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken)时最后面的accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;时accessTokenEnhancer为空,导致直接返回生成的UUID的token返回客户端。

    解决方案:

    解决方式可以采用一下两种方式:

    • configure(AuthorizationServerEndpointsConfigurer endpoints)方法中去掉endpoints.tokenServices(tokenServices())这个配置,删除tokenService配置,只保留endpoint的配置。即:
    @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager);
            //通过TokenEnhancerChain增强器链将jwtAccessTokenConverter(转换成jwt)和jwtTokenEnhancer(往里面加内容加信息)连起来
            TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = Lists.newArrayList();
            enhancerList.add(tokenEnhancer());
            enhancerList.add(accessTokenConverter());
            enhancerChain.setTokenEnhancers(enhancerList);
            endpoints
                .tokenEnhancer(enhancerChain)
                .accessTokenConverter(accessTokenConverter());
    
        }
    
    • endpoints配置 tokenEnhancer的配置代码删除,落到配置tokenService()方法的配置中去即:
     @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager);
            endpoints
                .tokenServices(tokenServices())
                .accessTokenConverter(accessTokenConverter());
    
        }
    
        @Primary
        @Bean
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
            defaultTokenServices.setTokenStore(tokenStore());
            //维持刷新token
            defaultTokenServices.setSupportRefreshToken(true);
            //通过TokenEnhancerChain增强器链将jwtAccessTokenConverter(转换成jwt)和jwtTokenEnhancer(往里面加内容加信息)连起来
            TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = Lists.newArrayList();
            enhancerList.add(tokenEnhancer());
            enhancerList.add(accessTokenConverter());
            enhancerChain.setTokenEnhancers(enhancerList);
            defaultTokenServices.setTokenEnhancer(enhancerChain);
            return defaultTokenServices;
        }
    

    相关文章

      网友评论

        本文标题:SpringSecurity+JWT配置的坑以及源码分析

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