Spring cloud系列20 OAuth2.0的实现客户端模式(client_credentials)支持refesh code

2,371 阅读2分钟

默认情况下OAuth2.0 客户端模式(client_credentials)不支持refresh code。现在由于业务的关系,需要支持refresh code。

在Spring OAuth2.0中 client_credentials模式对应的类是ClientCredentialsTokenGranter 在此类中有个变量可以控制是否返回refreshcode,此成员变量是allowRefresh,默认值为false。在此类的在grant()方法中,如果allowRefresh=false,则会将OAuth2AccessToken实例中的refreshCode值设置为null。所以如果要client_credentials模式返回refreshcode,则只需要调用setAllowRefresh()设置allowRefresh为true即可。

ClientCredentialsTokenGranter.java源码如下:

public class ClientCredentialsTokenGranter extends AbstractTokenGranter {

        private static final String GRANT_TYPE = "client_credentials";
        private boolean allowRefresh = false;

        // 可以设置
        public void setAllowRefresh(boolean allowRefresh) {
                this.allowRefresh = allowRefresh;
        }

        @Override
        public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
                OAuth2AccessToken token = super.grant(grantType, tokenRequest);
                if (token != null) {
                        DefaultOAuth2AccessToken norefresh = new DefaultOAuth2AccessToken(token);
                        // The spec says that client credentials should not be allowed to get a refresh token
                        if (!allowRefresh) {
                                // 删除refresh code的值
                                norefresh.setRefreshToken(null);
                        }
                        token = norefresh;
                }
                return token;
        }

}

那么系统在哪里初始化ClientCredentialsTokenGranter 值呢?经过debug后,发现在AuthorizationServerEndpointsConfigurer的私有方法tokenGranter中。此方法在调用时,如果发现tokenGranter 为空,则进行初始化。

private TokenGranter tokenGranter() {
     if (tokenGranter == null) {
             tokenGranter = new TokenGranter() {
                     private CompositeTokenGranter delegate;

     @Override
     public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
             if (delegate == null) {
                      // 默认请求下,会调用此方法初始化Granter,重点是getDefaultTokenGranters()方法
                     delegate = new CompositeTokenGranter(getDefaultTokenGranters());
             }
             return delegate.grant(grantType, tokenRequest);
     }
     };
     }
     return tokenGranter;
}
//  在这个方法中初始化ClientCredentialsTokenGranter等Granter
private List<TokenGranter> getDefaultTokenGranters() {
     ClientDetailsService clientDetails = clientDetailsService();
     AuthorizationServerTokenServices tokenServices = tokenServices();
     AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
     OAuth2RequestFactory requestFactory = requestFactory();

     List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
     tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,
             requestFactory));
     tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
     ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
     tokenGranters.add(implicit);
     // 创建client_credentials模式的处理类
     tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
     if (authenticationManager != null) {
             tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
                     clientDetails, requestFactory));
     }
     return tokenGranters;
     }

现在我们要使用自己创建的TokenGranter,而不是默认值,AuthorizationServerEndpointsConfigurer有个 tokenGranter(TokenGranter)可以用来设置自定义的TokenGranter。通过这个方法设置TokenGranter后,调用tokenGranter()时发现tokenGranter已经有值,则不会进行初始化

public AuthorizationServerEndpointsConfigurer tokenGranter(TokenGranter tokenGranter) {
        this.tokenGranter = tokenGranter;
        return this;
}

最后,在OAuth2AuthorizationServer的configure(AuthorizationServerEndpointsConfigurer)方法中使用endpoints.tokenGranter()配置自定义granter。@Bean方法getCustomizedTokenGranters()方法返回自定义的TokenGranter的列表。 此类中其他实例都是自定义的组件替换系统默认的组件,这里略。

// 授权服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private MyClientDetailsService myClientDetailsService;

    @Autowired
    private RedisTokenStore redisTokenStore;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MyUserDetailsService myUserDetailsService;
    @Autowired
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 自定义granters
        endpoints.tokenGranter(new CompositeTokenGranter(getCustomizedTokenGranters()));
    }

    @Bean
    @Primary
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(redisTokenStore);
        tokenServices.setClientDetailsService(myClientDetailsService);
        return tokenServices;
    }

    private List<TokenGranter> getCustomizedTokenGranters() {
        AuthorizationServerTokenServices tokenServices = tokenServices();
        ClientDetailsService clientDetails = myClientDetailsService;
        AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
        OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetails);

        AuthorizationCodeTokenGranter authorizationCodeTokenGranter = new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory);
        RefreshTokenGranter refreshTokenGranter = new RefreshTokenGranter(tokenServices, clientDetails, requestFactory);
        ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
        ClientCredentialsTokenGranter clientCredentialsTokenGranter = new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory);
        // 设置返回refresh code
        clientCredentialsTokenGranter.setAllowRefresh(true); AuthorizationServerEndpointsConfigurer.getDefaultTokenGranters

        List<TokenGranter> tokenGranters = new ArrayList<>();
        tokenGranters.add(authorizationCodeTokenGranter);
        tokenGranters.add(refreshTokenGranter);
        tokenGranters.add(implicit);
        tokenGranters.add(clientCredentialsTokenGranter);
        if (authenticationManager != null) {
            tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory));
        }

        return tokenGranters;
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        // TODO 如果要使用这个值,则需要存储到redis中,https://blog.csdn.net/dong_19890208/article/details/74914852
        return new InMemoryAuthorizationCodeServices();
    }

}

改造完成进行测试代码:

在这里插入图片描述
测试结果,使用获取refresh_token值成功执行刷新:
在这里插入图片描述