我让Spring Cloud Gateway与KeyCloak一起作为OpenId连接提供程序。如果我使用Postman在网关上(而不是在某些后端服务上(遇到REST端点,那么当我遇到端点时,它不会生成会话Cookie。但是,如果我使用Chrome浏览器访问了同一个端点,则对该端点的响应会生成一个会话cookie。因此,请求缓存不适用于Postman,而适用于chrome。我尝试过在后端访问REST端点,但不清楚为什么它可以与浏览器一起使用,而不能与Postman(linux客户端(一起使用。密钥斗篷验证部分工作正常。
例如,当我尝试使用poster访问localhost:8080/live时,不会为该请求生成会话cookie,并且用户代理会被重定向到localhost:8080/oauth2/authorization/keyclock registration(在同一网关服务上(。然后,该端点重定向到keycloft(授权端点(并设置会话cookie。然而,由于该端点对原始请求不敏感,因此不会发生请求缓存;它只是重定向到"/"在随后的身份验证成功时在网关上。
OTOH,如果我使用chrome,我的原始请求(to/live(会生成一个会话cookie,并重定向到localhost:8080/oauth2/authorization/key斗篷注册不会生成任何额外的会话cookie。随后与密钥斗篷的身份验证交互工作正常,请求缓存工作正常。
我的Spring Security配置如下。有趣的是,与SpringMVC中的HttpSecurity不同,ServerHttpSecurity没有为会话管理提供任何旋钮。因此,我不知道如何以确定性的方式使用户代理之间的行为统一。
@Configuration
@EnableWebFluxSecurity
public class AndurilSecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
.oauth2Login(withDefaults())
.requestCache();
http.csrf().disable();
return http.build();
}
}
application.yml的相关摘录以及一些编辑,
spring.cloud.gateway:
httpclient:
wiretap: true
httpserver:
wiretap: true
default-filters:
- name: BasicAuthFilter
routes:
- id: adminservice
uri: http://${ADMIN_SERVICE}/
predicates:
- Path=/admin/**
- id: appservice
uri: http://${APP_SERVICE}/
predicates:
- Path=/app/**
spring.security.oauth2.client:
provider:
keycloak:
issuer-uri: http://${AUTHSERVER}/auth/realms/${REALM}
user-name-attribute: preferred_username
registration:
keycloak-registration:
provider: keycloak
client-id: ${CLIENT_ID}
client-secret: ${CLIENT_SECRET}
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/keycloak"
问题似乎出在媒体类型上。由于某些原因,*/*
被拒绝/忽略,但text/html
工作正常。目前尚不清楚为什么MediaTypes会在请求缓存中扮演任何角色。
WITH CHROME:
-------------------------------------------
03:59:49.049 reactor-http-epoll-2 DEBUG anduril NegatedServerWebExchangeMatcher: matches = true
03:59:49.049 reactor-http-epoll-2 DEBUG anduril AndServerWebExchangeMatcher: Trying to match using MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[*/*]]
03:59:49.049 reactor-http-epoll-2 DEBUG anduril MediaTypeServerWebExchangeMatcher: httpRequestMediaTypes=[text/html, application/xhtml+xml, image/avif, image/webp, application/xml;q=0.9, */*;q=0.8]
03:59:49.049 reactor-http-epoll-2 DEBUG anduril MediaTypeServerWebExchangeMatcher: Processing text/html
03:59:49.049 reactor-http-epoll-2 DEBUG anduril MediaTypeServerWebExchangeMatcher: text/html .isCompatibleWith text/html = true
03:59:49.049 reactor-http-epoll-2 DEBUG anduril AndServerWebExchangeMatcher: All requestMatchers returned true
03:59:49.049 reactor-http-epoll-2 DEBUG anduril WebSessionServerRequestCache: Request added to WebSession: '/live'
03:59:49.049 reactor-http-epoll-2 DEBUG anduril DefaultServerRedirectStrategy: Redirecting to '/oauth2/authorization/keycloak-registration'
03:59:49.049 reactor-http-epoll-2 DEBUG anduril PathPatternParserServerWebExchangeMatcher: Checking match of request : '/oauth2/authorization/keycloak-registration'; against '/oauth2/authorization/{registrationId}'
WITH POSTMAN USING */*:
-------------------------------------------
04:30:44.044 parallel-3 DEBUG anduril NegatedServerWebExchangeMatcher: matches = true
04:30:44.044 parallel-3 DEBUG anduril AndServerWebExchangeMatcher: Trying to match using MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[*/*]]
04:30:44.044 parallel-3 DEBUG anduril MediaTypeServerWebExchangeMatcher: httpRequestMediaTypes=[*/*]
04:30:44.044 parallel-3 DEBUG anduril MediaTypeServerWebExchangeMatcher: Processing */*
04:30:44.044 parallel-3 DEBUG anduril MediaTypeServerWebExchangeMatcher: Ignoring
04:30:44.044 parallel-3 DEBUG anduril MediaTypeServerWebExchangeMatcher: Did not match any media types
04:30:44.044 parallel-3 DEBUG anduril AndServerWebExchangeMatcher: Did not match
04:30:44.044 parallel-3 DEBUG anduril DefaultServerRedirectStrategy: Redirecting to '/oauth2/authorization/keycloak-registration'
04:30:44.044 reactor-http-epoll-4 DEBUG anduril PathPatternParserServerWebExchangeMatcher: Checking match of request : '/oauth2/authorization/keycloak-registration'; against '/oauth2/authorization/{registrationId}'
WITH POSTMAN USING text/html:
-------------------------------------------
04:47:30.030 parallel-1 DEBUG anduril NegatedServerWebExchangeMatcher: matches = true
04:47:30.030 parallel-1 DEBUG anduril AndServerWebExchangeMatcher: Trying to match using MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[*/*]]
04:47:30.030 parallel-1 DEBUG anduril MediaTypeServerWebExchangeMatcher: httpRequestMediaTypes=[text/html]
04:47:30.030 parallel-1 DEBUG anduril MediaTypeServerWebExchangeMatcher: Processing text/html
04:47:30.030 parallel-1 DEBUG anduril MediaTypeServerWebExchangeMatcher: text/html .isCompatibleWith text/html = true
04:47:30.030 parallel-1 DEBUG anduril AndServerWebExchangeMatcher: All requestMatchers returned true
04:47:30.030 parallel-1 DEBUG anduril WebSessionServerRequestCache: Request added to WebSession: '/live'
04:47:30.030 parallel-1 DEBUG anduril DefaultServerRedirectStrategy: Redirecting to '/oauth2/authorization/keycloak-registration'
04:47:30.030 reactor-http-epoll-3 DEBUG anduril PathPatternParserServerWebExchangeMatcher: Checking match of request : '/oauth2/authorization/keycloak-registration'; against '/oauth2/authorization/{registrationId}'
根据您的配置,我可以排除任何与Spring Cloud Gateway和OAuth 2.0支持相关的内容。对于默认请求缓存实现WebSessionServerRequestCache
,这是一种更微妙的行为。
TL;DR——如果您在Postman中将Accept: text/html
添加到页眉中,它应该可以工作。
Spring Security的webflux支持中的请求缓存被设计为(类似于servlet(仅适用于来自浏览器的web请求。由于Postman并不完全是一个浏览器,它不属于Spring Security开箱即用支持的情况,这解释了为什么您在Postman中的行为不同。像curl这样的命令行客户端也属于这一类,原生应用程序等也是如此。
Postman默认发送*/*
的Accept
报头;给我任何回应"Spring Security忽略了这一点,并进一步明确地期望Accept
标头为text/html
,这是在典型的浏览器请求中发送的。请参见这些行。