Spring云网关+Spring安全资源服务器

Spring Cloud Gateway + Spring security resource server

我真的不会把它放在这里,但我真的很困惑, 我想实现以下目标。

我是运行

现在我想将安全性集成到我的网关和所有下游微服务中。最终,我决定使用 Firebase 作为身份提供者 (IDP)。我的 Angular 应用程序将从 Firebase 获取 JWT 令牌,并在每个请求中将其发送到 Cloud Gateway。因此,网关将开始仅充当资源服务器,仅此而已。

这是我尝试尝试的方式。 设置和Spring Cloud Gateway同时充当Resource Server。 这里解释得很好Spring Security Docs

这是我的配置

@EnableWebFluxSecurity
public class ResourceServerSecurityConfiguration {

  @Bean
  public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    // @formatter:off
    http
        .authorizeExchange()
        .anyExchange().authenticated()
        .and()
        .oauth2ResourceServer()
        .jwt();
    return http.build();
    // @formatter:on
  }
}

和application.yml

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com
          issuer-uri: https://securetoken.google.com/{$app.name}

如您在此 YAML 中所见,我提供了 jwk-set-uri 和发行者来验证传入的令牌。

至此,所有的工作都可以接受了。所有请求都必须在身份验证中具有有效的 JWT header。

接下来,

我希望我的网关使用 WebClient 并调用多个服务来为前端聚合数据。

这是我尝试配置客户端的方式。

  @Bean
  @LoadBalanced
  public WebClient.Builder loadBalancedWebClientBuilder() {
    return WebClient.builder()
        .filter(new ServletBearerExchangeFilterFunction());
  }

如您所见,它使用 ServletBearerExchangeFilterFunction 这就是我真正的问题所在。

我已经检查过 Spring 配置 oauth2ResourceServer 时它使用 NoOpServerSecurityContextRepository。到目前为止,据我了解,这正是一个用于为每个请求注册上下文的存储库。另外,我知道使用 NoOp 是有意义的,因为我们想要无状态。但是我不明白如何使 ServletBearerExchangeFilterFunction 正常工作并向下游传递我的令牌。

我现在花了很多时间试图找出正确的方法。

找到这个:

Github: https://github.com/spring-projects/spring-security/issues/7771

即使根据这个,我尝试做的事情也应该是合法且可能的。不知道我错在哪里。

Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.

如果您将 SCG(Spring Cloud Gateway) 设置为 oauth2 资源服务器,您必须做更多的自定义,也许像 this。我认为你不应该那样做。

你可以使用网关作为路由,将access token header发送给SCG,SCG将access token拿到oauth2资源服务器,并在oauth2资源服务器端检查权限。

Spring Cloud Gateway Token Relay GatewayFilter Factory 说:

The {githubmaster}/src/main/java/org/springframework/cloud/gateway/security/TokenRelayGatewayFilterFactory.java[filter] extracts an access token from the currently authenticated user, and puts it in a request header for the downstream requests.

这样可以在您的应用程序中保持 oauth2 的清晰度。

我想通了,问题是 ReactiveSecurityContext 仅在您处于反应流中时可用,而 ServletBearerExchangeFilterFunction 用于 Servlet 调用。

最终,我编写了自己的过滤器函数,它监听 ReactiveSecurity 上下文并设置授权 header。在这里。

public class BearerExchangeFilterFunction implements ExchangeFilterFunction {

  @Override
  @NonNull
  public Mono<ClientResponse> filter(@NonNull ClientRequest request, ExchangeFunction next) {
    return ReactiveSecurityContextHolder.getContext()
        .map(c -> (c.getAuthentication().getCredentials()))
        .cast(AbstractOAuth2Token.class)
        .checkpoint()
        .map(token -> bearer(request, token))
        .defaultIfEmpty(request)
        .flatMap(next::exchange);
  }

  private ClientRequest bearer(ClientRequest request, AbstractOAuth2Token token) {
    return ClientRequest.from(request)
        .headers(headers -> headers.setBearerAuth(token.getTokenValue()))
        .build();
  }


}