简体   繁体   English

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

[英]Spring Cloud Gateway + Spring security resource server

I really would not put it here but I am really confused, I want to achieve the following.我真的不会把它放在这里但我真的很困惑,我想实现以下目标。

I am running我在跑步

  • Java 14 Java 14
  • Spring Cloud Gateway version: Hoxton.SR3 Spring Cloud Gateway版本: Hoxton.SR3
  • Spring Boot version: 2.2.5.RELEASE Spring Boot版本: 2.2.5.RELEASE

Now I want to integrate security to my Gateway and to all the downstream microservices.现在我想将安全性集成到我的网关和所有下游微服务中。 Eventually, I decided to go with Firebase as an Identity Provider (IDP).最终,我决定将 go 与 Firebase 作为身份提供者 (IDP)。 My Angular application will get JWT token from Firebase and send it in every request to Cloud Gateway.我的 Angular 应用程序将从 Firebase 获取 JWT 令牌,并将其在每个请求中发送到云网关。 So, the Gateway will start to act as a resource server ONLY and that is it.因此,网关将开始仅充当资源服务器,仅此而已。

Here how I tried to give it a go.在这里,我如何尝试给它一个 go。 Set up and Spring Cloud Gateway to act like Resource Server at the same time.同时设置Spring Cloud Gateway充当资源服务器。 Quite well explained here Spring Security Docs . Spring Security Docs在这里解释得很好。

Here what my configuration looks like这是我的配置的样子

@EnableWebFluxSecurity
public class ResourceServerSecurityConfiguration {

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

And application.yml和 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}

As you see in this YAML I provided the jwk-set-uri and issuer to validate incoming tokens.正如您在此 YAML 中看到的,我提供了 jwk-set-uri 和颁发者来验证传入的令牌。

At this point, all work quite as accepted.在这一点上,所有的工作都被接受了。 All the requests have to have valid JWT in the Authentication header.所有请求都必须在身份验证 header 中具有有效的 JWT。

Next,下一个,

I want my gateway to use WebClient and call several services to aggregate data for the frontend.我希望我的网关使用WebClient并调用多个服务来为前端聚合数据。

Here how I am trying to configure my client.这是我尝试配置客户端的方式。

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

As you see It uses ServletBearerExchangeFilterFunction this is where my real problem comes in.如您所见,它使用ServletBearerExchangeFilterFunction这是我真正的问题所在。

I already checked that when Spring configuring oauth2ResourceServer it uses NoOpServerSecurityContextRepository .我已经检查过,当 Spring 配置 oauth2ResourceServer 时,它使用NoOpServerSecurityContextRepository From what I understand so far that this is exactly a repository that used to register context per request.据我所知,这正是一个用于为每个请求注册上下文的存储库。 Also, I understand that it makes sense to use NoOp as we want to be stateless.另外,我知道使用 NoOp 是有意义的,因为我们希望是无状态的。 However what I do not understand how to make ServletBearerExchangeFilterFunction to work properly and pass downstream my tokens.但是,我不明白如何使ServletBearerExchangeFilterFunction正常工作并将我的令牌传递到下游。

I spend now quite a lot of time trying to figure out the correct way of doing this.我现在花了很多时间试图找出正确的方法。

Found this: Spring Boot 2 OIDC (OAuth2) client / resource server not propagating the access token in the WebClient发现这个: Spring Boot 2 OIDC (OAuth2) 客户端/资源服务器未在 WebClient 中传播访问令牌

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

And even according to this what I try to do should be legit and possible.即使根据这一点,我尝试做的事情也应该是合法和可能的。 Not sure where I am mistaken.不知道我错在哪里。

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. Spring 云网关旨在提供一种简单而有效的方式来路由到 API 并为它们提供横切关注点,例如:安全性、监控/指标和弹性。

If you set up SCG(Spring Cloud Gateway) as oauth2 resource server you must do more custom,Maybe like this .如果你将 SCG(Spring Cloud Gateway) 设置为 oauth2 资源服务器,你必须做更多的自定义,也许像这样 I think you should not do that.我认为你不应该那样做。

You can use gateway use as route,and send access token header to SCG,and SCG will take access token to oauth2 resource server,and check permission on oauth2 resource server side.您可以使用网关作为路由,并将访问令牌header发送到SCG,SCG将访问令牌带到oauth2资源服务器,并检查oauth2资源服务器端的权限。

Spring Cloud Gateway Token Relay GatewayFilter Factory say: Spring 云网关令牌中继网关过滤器工厂说:

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. {githubmaster}/src/main/java/org/springframework/cloud/gateway/security/TokenRelayGatewayFilterFactory.java[filter] 从当前经过身份验证的用户中提取访问令牌,并将其放入下游请求的请求 header 中。

So that can keep oauth2 clearness in you app.这样可以在您的应用程序中保持 oauth2 的清晰性。

I figured this, the thing is that ReactiveSecurityContext is available only when you are within the reactive flow and ServletBearerExchangeFilterFunction meant to be for Servlet calls.我想这一点,事情是 ReactiveSecurityContext 仅当您在反应流中可用并且 ServletBearerExchangeFilterFunction 用于 Servlet 调用时才可用。

Eventually, I just wrote my own filter function which listens to ReactiveSecurity context and sets the Authorization header.最后,我只写了自己的过滤器 function,它监听 ReactiveSecurity 上下文并设置授权 header。 Here it is.这里是。

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();
  }


}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM