【问题标题】:Failing to enable Spring Authorization Server token request with JSON request body无法使用 JSON 请求正文启用 Spring Authorization Server 令牌请求
【发布时间】:2021-12-03 11:03:00
【问题描述】:

我正在使用 Spring Authorization Server 开发 OAuth2 授权服务器。我需要支持客户端凭据流,并且我希望客户端在 POST 请求的 JSON 请求正文中发送客户端 ID 和密码。

我的配置非常简单。基本上都是带有自定义RegisteredClientRepository的默认设置:

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {
    @Bean
    @Primary
    public RegisteredClientRepository registeredClientRepository(UserRepository repository) {
        return repository;
    }
}

UserRepository 中的findByClientId 方法如下所示:

@Repository
@RequiredArgsConstructor
public class UserRepository implements RegisteredClientRepository {
    private final UserDao UserDao;

    @Override
    public RegisteredClient findByClientId(String clientId) {
        return userDao.findByClientId(clientId)
            .map(this::toRegisteredClient)
            .orElse(null);
    }

    private RegisteredClient toRegisteredClient(User user) {
        return RegisteredClient.withId(String.valueOf(user.getId()))
            .clientName(user.getName())
            .clientId(user.getClientId())
            .clientSecret(user.getClientSecret())
            .clientAuthenticationMethod(CLIENT_SECRET_BASIC)
            .clientAuthenticationMethod(CLIENT_SECRET_POST)
            .authorizationGrantType(CLIENT_CREDENTIALS)
            .scope("TEST")
            .clientSettings(
                ClientSettings.builder().requireAuthorizationConsent(false).build()
            )
            .build();
    }
}

假设客户端 ID 为 user,秘密为 password

使用基本身份验证请求令牌有效:

curl -X POST --header "Authorization: Basic dXNlcjpwYXNzd29yZA==" http://localhost:8080/oauth2/token\?grant_type\=client_credentials

我还可以使用请求 URI 参数请求令牌:

curl -X POST http://localhost:8080/oauth2/token\?grant_type\=client_credentials\&client_id\=user\&client_secret\=password

但是当我尝试像这样在请求正文中以 JSON 格式发送凭据时,我收到 HTTP 401 错误:

curl --header "Content-Type: application/json" -d '{"grant_type": "client_credentials", "client_id": "user", "client_secret": "password"}' http://localhost:8080/oauth2/token

这是服务器端记录的内容:

DEBUG 2021-10-18 16:04:31,287 [393-exec-4] FilterChainProxy - Securing POST /oauth2/token
DEBUG 2021-10-18 16:04:31,287 [393-exec-4] SecurityContextPersistenceFilter - Set SecurityContextHolder to empty SecurityContext
DEBUG 2021-10-18 16:04:31,287 [393-exec-4] AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
DEBUG 2021-10-18 16:04:31,288 [393-exec-4] FilterSecurityInterceptor - Failed to authorize filter invocation [POST /oauth2/token] with attributes [authenticated]
DEBUG 2021-10-18 16:04:31,288 [393-exec-4] HttpSessionSecurityContextRepository - Did not store empty SecurityContext
DEBUG 2021-10-18 16:04:31,288 [393-exec-4] SecurityContextPersistenceFilter - Cleared SecurityContextHolder to complete request

我该如何让它发挥作用?

另外,为什么 Spring Authorization Server 支持将客户端凭据作为请求 URI 参数发送? OAuth 2.1 规范的Section 2.3 明确规定不得使用请求 URI 参数,因为这被认为是不安全的。

【问题讨论】:

  • 能否提供服务器的调试日志。
  • @Toerktumlare 我添加了当它给我 HTTP 401 时获得的服务器日志

标签: java spring spring-security oauth-2.0


【解决方案1】:

您可以为您的客户端身份验证配置提供AuthenticationConverter,如下所示:

   @Bean
   @Order(1)
   public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
      OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
            new OAuth2AuthorizationServerConfigurer<>();
      RequestMatcher endpointsMatcher = authorizationServerConfigurer
            .getEndpointsMatcher();
      authorizationServerConfigurer.clientAuthentication((clientAuth) -> 
            clientAuth.authenticationConverter(myCustomAuthenticationConverter()) // provide your auth converter
      );

//    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); the code above is copied from here

      http
            .requestMatcher(endpointsMatcher)
            .authorizeRequests(authorizeRequests ->
                  authorizeRequests.anyRequest().authenticated()
            )
            .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
            .apply(authorizationServerConfigurer);

      return http.formLogin(Customizer.withDefaults()).build();
   }

【讨论】:

  • 我知道这是如何工作的。对我来说,这似乎是一种解决方法,应该是默认行为。
  • 我没有在文档中清楚地看到这一点。您可以随时与项目的创建者交谈。请在the repository 提交问题以澄清您的问题,它甚至可能成为新功能:)
猜你喜欢
  • 1970-01-01
  • 2017-03-04
  • 2014-11-05
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 1970-01-01
  • 2016-11-01
  • 1970-01-01
相关资源
最近更新 更多