如何通过访问令牌响应设置Spring Security中OAuth2AuthorizedClient的主体名称?
嘿,我来帮你解决这个问题!你现在用的是Spring Boot 3搭配Spring Security 6的OAuth2客户端,用的是authorization_code授权码模式,想要从访问令牌的响应里自定义设置OAuth2AuthorizedClient的主体名称对吧?
默认情况下,Spring Security要么从ID Token的sub字段提取主体名称,要么会调用用户信息端点去获取。但如果你的授权服务器把主体信息直接放在access token的响应体里(比如某个自定义字段),那我们就得自己写点逻辑来搞定这个事。
下面是具体的实现步骤,结合你的现有配置来改:
第一步:自定义令牌响应转换器
我们需要先写一个转换器,从授权服务器返回的token响应里提取出我们要的主体字段,把它存到access token的额外参数里方便后续使用:
public class CustomAccessTokenResponseConverter implements OAuth2AccessTokenResponseConverter { // 用默认转换器处理基础的token响应 private final OAuth2AccessTokenResponseConverter defaultConverter = new DefaultOAuth2AccessTokenResponseConverter(); @Override public OAuth2AccessTokenResponse convert(Map<String, Object> tokenResponseParameters) { OAuth2AccessTokenResponse response = defaultConverter.convert(tokenResponseParameters); // 这里替换成你实际令牌响应里的主体字段名,比如user_id、username或者sub String principalName = (String) tokenResponseParameters.get("user_id"); // 把主体名称存到access token的额外参数中 return OAuth2AccessTokenResponse.withResponse(response) .additionalParameters(Map.of("custom_principal", principalName)) .build(); } }
第二步:修改Security配置,绑定自定义逻辑
接下来我们要把上面的转换器集成到你的Security配置里,同时自定义OAuth2AuthorizedClientManager来设置主体名称:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 初始化自定义的token响应客户端 DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient(); tokenResponseClient.setAccessTokenResponseConverter(new CustomAccessTokenResponseConverter()); http.authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated() ).oauth2Client(oauth2 -> oauth2 .authorizationCodeGrant(codeGrant -> codeGrant .accessTokenResponseClient(tokenResponseClient))) .formLogin(Customizer.withDefaults()); return http.build(); } // 自定义OAuth2AuthorizedClientManager,用来设置自定义主体名称 @Bean public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService); // 替换默认的授权客户端提供者,加入自定义逻辑 authorizedClientManager.setAuthorizedClientProvider(customAuthorizedClientProvider()); return authorizedClientManager; } private OAuth2AuthorizedClientProvider customAuthorizedClientProvider() { // 构建默认的授权码模式提供者 DefaultAuthorizationCodeOAuth2AuthorizedClientProvider codeProvider = (DefaultAuthorizationCodeOAuth2AuthorizedClientProvider) OAuth2AuthorizedClientProviderBuilder.builder().authorizationCode().build().getAuthorizationCode(); // 包装提供者,自定义授权客户端的构建逻辑 return new OAuth2AuthorizedClientProvider() { @Override public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) { OAuth2AuthorizedClient authorizedClient = codeProvider.authorize(context); if (authorizedClient != null) { // 从access token的额外参数里取出之前存的主体名称 String customPrincipal = (String) authorizedClient.getAccessToken().getAdditionalParameters().get("custom_principal"); // 重新构建OAuth2AuthorizedClient,替换默认的主体名称 return new OAuth2AuthorizedClient( authorizedClient.getClientRegistration(), customPrincipal, authorizedClient.getAccessToken(), authorizedClient.getRefreshToken()); } return null; } }; } }
小提示
你只需要把代码里的user_id替换成你实际令牌响应中对应的主体字段名就行,比如如果你的响应里主体字段是username或者sub,直接改tokenResponseParameters.get("user_id")里的参数名就可以了。
备注:内容来源于stack exchange,提问作者AwesomeDude091
相关产品推荐
相关产品推荐

