我将在一些现有的 Web 应用程序前面放置一个 Spring Cloud 网关,这些应用程序已经使用 keycloak 作为其身份提供者,并且我想对网关内的传入请求进行身份验证。目前,每个 Web 应用程序都已配置了正确的客户端 ID,并使用正确的值重定向到 keycloak。现在,网关必须执行授权代码流程而不是每个应用程序,因此它必须提前知道哪个客户端对应哪个请求的 URL。 所以,我正在研究如何实现它,但我仍然在这里没有任何适当的解决方案。 有什么解决办法吗?或者,这样做真的是网关的责任吗?
实际上,我找到了一个解决方案,但我不确定它是否是最好的。
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange().pathMatchers("/actuator/**").permitAll().and()
.authorizeExchange().anyExchange().authenticated().and().csrf().disable().oauth2Login()
.and()
.exceptionHandling().authenticationEntryPoint(createEntryPoints())
.and()
.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(grantedAuthoritiesExtractor());
return http.build();
}
public ServerAuthenticationEntryPoint createEntryPoints() {
List<DelegateEntry> entryPoints = new ArrayList<>();
entryPoints
.add(new DelegateEntry(ServerWebExchangeMatchers.pathMatchers("/app1"),
new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/client1")));
//other clients will be added here
DelegatingServerAuthenticationEntryPoint defaultEntryPoint = new DelegatingServerAuthenticationEntryPoint(
entryPoints);
defaultEntryPoint.setDefaultEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED));
return defaultEntryPoint;
}
因此 client1 将用于 /app1 等等。 正如我之前所说,我不确定,可能有更好的解决方案。
对于基于 Spring Boot 和 Spring Cloud Gateway 的网关,必要的步骤概述如下。
1。定义您的 OAuth2 客户端
首先您需要定义 OAuth2 客户端。最简单的方法是依靠自动配置(参见 org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientConfigurations)并将它们放入
application.yml
:
spring:
security:
oauth2:
client:
provider:
keycloak:
issuer-uri: ...
registration:
client-1:
provider: keycloak
client-id: ...
client-secret: ...
client-2:
provider: keycloak
client-id: ...
client-secret: ...
2。配置TokenRelay过滤器
您需要确保 TokenRelay 过滤器 传递用于将 OAuth2 客户端匹配的令牌到您的后端服务。同样,这可以在
application.yml
中完成:
spring:
cloud:
gateway:
routes:
- id: route-client-1
uri: ...
predicates:
- Path=/client1/**
filters:
- TokenRelay=client-1
- id: route-client-2
uri: ...
predicates:
- Path=/client2/**
filters:
- TokenRelay=client-2
当与之前的代码片段合并时,请确保有单个根元素
spring
。
3.定义单独的
SecurityWebFilterChain
s
最后您需要定义单独的安全 Web 过滤器链。最大的原因是定义初始化 OAuth2 流的不同端点。因为否则 Spring Security 会呈现一个 HTML 页面,用户需要在其中显式选择正确的客户端注册。
@Order(1)
@Bean
public SecurityWebFilterChain springSecurityFilterChainClient2(
ServerHttpSecurity http
) {
http.securityMatcher(
new OrServerWebExchangeMatcher(
new PathPatternParserServerWebExchangeMatcher("/client2/**"),
new PathPatternParserServerWebExchangeMatcher("/oauth2/authorization/client-2"),
new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/client-2")
)
)
.authorizeExchange(
authorizeExchangeSpec ->
authorizeExchangeSpec.anyExchange().authenticated()
)
.exceptionHandling(
exceptionHandlingSpec ->
exceptionHandlingSpec.authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/client-2"))
)
.oauth2Login(Customizer.withDefaults());
return http.build();
}
@Order(2)
@Bean
public SecurityWebFilterChain springSecurityFilterChain(
ServerHttpSecurity http
) {
http
.securityMatcher(
new OrServerWebExchangeMatcher(
new PathPatternParserServerWebExchangeMatcher("/client1/**"),
new PathPatternParserServerWebExchangeMatcher("/oauth2/authorization/client-1"),
new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/client-1")
)
)
.authorizeExchange(
authorizeExchangeSpec ->
authorizeExchangeSpec.anyExchange().authenticated()
)
.exceptionHandling(
exceptionHandlingSpec ->
exceptionHandlingSpec.authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/client-1"))
)
.oauth2Login(Customizer.withDefaults());
return http.build();
}
你可以看看这个答案:
如何创建Spring Cloud网关过滤器来添加客户端凭据访问令牌?
为了支持不同的 client-id(secrets、token-uris 等),您可以在 spring.security.oauth2.client .registration 部分定义多个配置,并在 Oauth2ClientGatewayFilter 中使 clientid 动态化班级:
String clientId = ...
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientId)
.principal("myPrincipal").build();