配置 Spring Security 5 Oauth 2 以使用 access_token uri 参数

Configure Spring Security 5 Oauth 2 to use access_token uri parameter

我正在基于此示例创建应用程序 -

背景 -

https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2resourceserver-webflux

OAuth2 令牌在 Header 中,它工作得很好。

问题-

但是我想将其更改为在 url 中使用 OAuth 2 令牌。我正在尝试创建一个 OAuth2 资源服务器。

分析-

似乎Spring安全支持从access_token参数获取令牌-

https://github.com/spring-projects/spring-security/blob/e3eaa99ad06769cf44ad3e1249f6398077b90834/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java#L57

不过好像默认是禁用的-

https://github.com/spring-projects/spring-security/blob/master/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java#L48

现在这个class在spring层级之外是不可访问的直接在这里创建-

https://github.com/spring-projects/spring-security/blob/master/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java#L955

有问题吗?

是否可以在我的代码中将此 allowUriQueryParameter 设置为 true?

更新

我正在创建一个 OAuth2 资源服务器。不幸的是,OAuth2ResourceServerSpec 不允许设置 authenticationConverter。

你可以查看Spring Security

的官方文档

您可以创建 new ServerBearerTokenAuthenticationConverter(),在 ServerHttpSecurity 中设置 allowUriQueryParameter 寄存器。

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .oauth2Login()
            .authenticationConverter(converter) // set 
            .authenticationManager(manager)
            .authorizedClientRepository(authorizedClients)
            .clientRegistrationRepository(clientRegistrations);
    return http.build();
}

作为解决方法,您可以编写 WebFilter 将查询参数转换为 Header。

Spring MVC 有很多 WebFlux 所缺少的合理默认值。这是其中之一。

我通过设置一个 WebFilter 来解决这个问题,该 WebFilter 在查询字符串中获取标记并将它们添加到 header。

@Override
@NonNull
public Mono<Void> filter(
  @NonNull ServerWebExchange serverWebExchange, @NonNull WebFilterChain 
     webFilterChain) {
  ServerHttpRequest request = serverWebExchange.getRequest();
  return webFilterChain.filter(
    serverWebExchange.mutate().request(sanitizeRequest(request)).build());
}

private ServerHttpRequest sanitizeRequest(ServerHttpRequest request) {
  MultiValueMap<String, String> queryParams = request.getQueryParams();
  ServerHttpRequest.Builder newRequest =
    request
        .mutate()
        .headers(
            headers -> {
              if (headerContainsAuthorizationKey(headers)) {
                String token = getAuthorizationBearer(headers);
                if (token != null) {
                  // "bearer" breaks WebFlux OAuth :)
                  headers.set(HttpHeaders.AUTHORIZATION, token.replace("bearer", "Bearer"));
                  return;
                }
              }
              if (pathContainsAccessToken(queryParams)) {
                headers.set(
                    HttpHeaders.AUTHORIZATION,
                    "Bearer " + queryParams.getFirst("access_token"));
              }
            });

  if (pathContainsAccessToken(queryParams)) {
    newRequest.uri(buildNewUriWithoutToken(request.getURI(), queryParams));
  }
  return newRequest.build();
 }
}

很高兴指出小写 "bearer" 也破坏了 WebFlux。

之后,您可以在安全链中的 oauthResourceServer 规范之前添加此过滤器:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(
  ServerHttpSecurity http,
  TokenStore tokenStore,
  AuthorizationHeaderFilter authorizationHeaderFilter) {
    http.csrf()
      .disable()
      .authorizeExchange()
      .anyExchange()
      .authenticated()
      .and()
      .addFilterAt(authorizationHeaderFilter, SecurityWebFiltersOrder.FIRST)
      .oauth2ResourceServer()
      .jwt() // ...  etc
  return http.build();
}

现在使用 Spring Security 5.1.5 我们可以做到这一点 -

ServerBearerTokenAuthenticationConverter 
authenticationConverter = new ServerBearerTokenAuthenticationConverter();
authenticationConverter.setAllowUriQueryParameter(true);

http.oauth2ResourceServer().bearerTokenConverter(authenticationConverter).jwt();

Pushkar 的答案对我不起作用,但帮助我找到了解决方案,以下代码可以解决问题:

DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
resolver.setAllowUriQueryParameter(true);

http.authorizeRequests()
        .anyRequest().authenticated()
        .and().oauth2ResourceServer().bearerTokenResolver(resolver)
        .jwt();

谢谢。