如何在Spring Security OAuth2中Token请求的Request Body中发送自定义参数

问题描述 投票:0回答:1

我正在开发一个用 Spring Boot (3.2.3) 和 Spring-Security (6.2.2) 编写的应用程序,该应用程序通过 REST 连接到外部服务(使用 Open Feign Client 实现)并使用 Bearer JWT 令牌进行身份验证。

用于外部服务的 JWT 令牌必须使用 OAuth2 获取。

外部服务的文档中解释了如何使用Postman获取jwt token,如下:

  • 授权类型:授权码
  • 回调网址:...
  • 客户 ID:...
  • 客户秘密:...
  • 授权网址:https://provider-name/authorize
  • 访问令牌网址:https://provider-name/token
  • 从高级中,在身份验证请求部分,设置“token_content_type”:“jwt”(这会转换为身份验证链接的查询参数)
  • 从高级中,在令牌请求部分,设置“token_content_type”:“jwt”,发送:“请求正文”。

使用 Postman 我成功检索了 JWT 令牌。 但是,我不确定如何用 Spring 完全实现这一点。

我的设置如下: 应用程序.yaml

spring:
  security:
    oauth2:
      client:
        registration:
          provider-name:
            client-id: ...
            client-secret: ...
            authorization-grant-type: authorization_code
            redirect-uri: "https://localhost:8080/a/token"
        provider:
          provider-name:
            authorization-uri: "https://provider-name/authorize"
            token-uri: "https://provider-name/token"

我的安全配置看起来像这样:

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         return http
                .authorizeHttpRequests(auth -> {
                    auth.requestMatchers("/a/token").permitAll();
                })
                 .oauth2Login(oauth2 ->
                         oauth2.authorizationEndpoint(endpoint ->
                                 endpoint.authorizationRequestResolver(new CustomAuthorizationRequestResolver(clientRegistrationRepository))))
                 .formLogin(withDefaults())
                 .build();
    }

其中 CustomAuthorizationRequestResolver 是:

@Component
public class CustomAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {

    private final OAuth2AuthorizationRequestResolver defaultResolver;

    public CustomAuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
        this.defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, "/oauth2/authorize");
    }

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
        OAuth2AuthorizationRequest authorizationRequest = this.defaultResolver.resolve(request);
        if (authorizationRequest != null) {
            authorizationRequest = customizeAuthorizationRequest(authorizationRequest);
        }
        return authorizationRequest;
    }

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
        OAuth2AuthorizationRequest authorizationRequest = this.defaultResolver.resolve(request, clientRegistrationId);
        if (authorizationRequest != null) {
            authorizationRequest = customizeAuthorizationRequest(authorizationRequest);
        }
        return authorizationRequest;
    }

    private OAuth2AuthorizationRequest customizeAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {
        if (!authorizationRequest.getAuthorizationUri().contains("provider-name")) {
            return authorizationRequest;
        }
        return OAuth2AuthorizationRequest.from(authorizationRequest)
                .additionalParameters(Map.of("token_content_type", "jwt"))
                .build();
    }

}

这对于设置身份验证请求的查询参数效果很好。

当我调用我的应用程序端点: https://localhost:8080/oauth2/authorize/provider-name 时,请求会正确转发给我的提供商,并且我会收到对 /a/token 的响应,其中的代码根据我的理解,我必须将其发送回 /token 端点。

但是,我不知道如何在Token Request的请求体上发送相同的键值对(token_conent_type/jwt)。 Spring Security 不应该这样做吗?如何拦截并附加我的自定义请求正文参数?

有什么建议吗?

谢谢!!

spring-boot spring-security spring-oauth2
1个回答
0
投票

首先,Spring 似乎希望重定向 uri 定义为:

"{baseUrl}/login/oauth2/code/provider-name"

因为我发现不同,框架无法拦截收到的身份验证代码并调用令牌端点。

其次,我做了以下改进:

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
        DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
        accessTokenResponseClient.setRequestEntityConverter(new CustomRequestEntityConverter());
        return accessTokenResponseClient;
    }

其中 CustomRequestEntityConverter 是:

public class CustomRequestEntityConverter implements Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> {

    private OAuth2AuthorizationCodeGrantRequestEntityConverter defaultConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter();

    @Override
    public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
        RequestEntity<?> defaultRequestEntity = defaultConverter.convert(authorizationCodeGrantRequest);
        if (defaultRequestEntity == null) {
            return null;
        }

        HttpHeaders headers = defaultRequestEntity.getHeaders();
        MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
        if (defaultRequestEntity.getBody() instanceof MultiValueMap) {
            formParameters.putAll((MultiValueMap<String, String>) defaultRequestEntity.getBody());
        }
        formParameters.add("token_content_type", "jwt");

        return new RequestEntity<>(formParameters, headers, defaultRequestEntity.getMethod(), defaultRequestEntity.getUrl());
    }
}

并将其链接到安全配置,如下所示:

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         return http
                 .oauth2Login(oauth2 ->
                         oauth2.authorizationEndpoint(endpoint ->
                                 endpoint.authorizationRequestResolver(new CustomAuthorizationRequestResolver(clientRegistrationRepository)))
                                 .tokenEndpoint(tokenEndpoint -> tokenEndpoint.accessTokenResponseClient(accessTokenResponseClient))
                 )
                 .build();
    }

就是这样。 快乐编码!

© www.soinside.com 2019 - 2024. All rights reserved.