如何在Spring Security中为联邦认证动态配置userInfoUri和tokenUri
动态调整OAuth2 Client的tokenUri和userInfoUri方案
可以实现根据授权码动态修改tokenUri和userInfoUri的需求,但要注意**ClientRegistration是不可变类**,无法直接修改已创建实例的属性,需要通过以下几种方式实现动态调整:
方案一:自定义ClientRegistrationRepository
放弃使用固定的InMemoryClientRegistrationRepository,自定义实现类,在每次获取ClientRegistration时根据当前请求的授权码动态生成对应URI:
@Bean public ClientRegistrationRepository clientRegistrationRepository() { return new ClientRegistrationRepository() { @Override public ClientRegistration findByRegistrationId(String registrationId) { // 从当前请求上下文获取授权码 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String authCode = request.getParameter("code"); // 根据授权码动态生成tokenUri和userInfoUri String dynamicTokenUri = buildTokenUri(authCode); String dynamicUserInfoUri = buildUserInfoUri(authCode); return ClientRegistration .withRegistrationId("project") .clientId("clientid") .clientSecret("clientsecret") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .redirectUri(redirectUri) .scope(Arrays.asList(scope.split(","))) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationUri(authorizationURI) .tokenUri(dynamicTokenUri) .userInfoUri(dynamicUserInfoUri) .userNameAttributeName(userNameAttributes) .build(); } }; } // 自定义生成URI的逻辑方法 private String buildTokenUri(String authCode) { // 根据授权码拼接或获取对应的token地址 return "https://example.com/token?code=" + authCode; } private String buildUserInfoUri(String authCode) { // 根据授权码拼接或获取对应的用户信息地址 return "https://example.com/userinfo?code=" + authCode; }
注意:要确保RequestContextHolder能正确获取当前请求上下文,在异步场景下可能需要额外处理线程绑定。
方案二:针对特定流程自定义客户端/服务类
如果不需要全局替换ClientRegistration,可以针对token请求和用户信息请求分别自定义处理逻辑:
1. 动态修改tokenUri(自定义TokenResponseClient)
@Bean public OAuth2AuthorizationCodeTokenResponseClient tokenResponseClient() { DefaultAuthorizationCodeTokenResponseClient tokenClient = new DefaultAuthorizationCodeTokenResponseClient(); tokenClient.setRestOperations(new RestTemplate() { @Override public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) { // 从授权请求中提取授权码 OAuth2AuthorizationCodeGrantTokenRequest tokenRequest = (OAuth2AuthorizationCodeGrantTokenRequest) request; String authCode = tokenRequest.getAuthorizationExchange().getAuthorizationResponse().getCode(); // 替换为动态生成的tokenUri String dynamicTokenUri = buildTokenUri(authCode); return super.postForObject(dynamicTokenUri, request, responseType, uriVariables); } }); return tokenClient; }
2. 动态修改userInfoUri(自定义OAuth2UserService)
@Bean public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() { DefaultOAuth2UserService userService = new DefaultOAuth2UserService(); userService.setRestOperations(new RestTemplate() { @Override public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) { // 从请求上下文获取OAuth2UserRequest,进而提取授权码 OAuth2UserRequest userRequest = (OAuth2UserRequest) RequestContextHolder.getRequestAttributes() .getAttribute("oauth2UserRequest", RequestAttributes.SCOPE_REQUEST); String authCode = userRequest.getAuthorizationExchange().getAuthorizationResponse().getCode(); // 替换为动态生成的userInfoUri String dynamicUserInfoUri = buildUserInfoUri(authCode); return super.getForObject(dynamicUserInfoUri, responseType, uriVariables); } }); return userService; }
关键注意事项
- 授权码的获取逻辑需根据实际流程调整:在授权回调阶段,code直接从请求参数获取;在后续token/userInfo请求阶段,需从OAuth2上下文对象中提取。
- 确保动态生成URI的逻辑线程安全,避免并发场景下的参数混乱。
- 适配Spring Security版本:上述代码基于Spring Security 5.x+,不同版本的类/接口可能存在差异。
内容的提问来源于stack exchange,提问作者Himanshu Sinha
相关产品推荐
相关产品推荐

