SpringSecurityOAuth2

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

2019-01-30  本文已影响0人  KingdomCoder

抛出问题:

之前在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返回客户端。

解决方案:

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

@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());

    }
 @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;
    }
上一篇下一篇

猜你喜欢

热点阅读