如何解决当我在授权标头中发送我的有效承载令牌时,为什么我没有通过我的 Spring-Cloud Gateway/OAuth2-Client 进行身份验证?
我正在开发一个微服务基础设施,并首先实现了一个 Spring Cloud Gateway 来代理我的所有请求。我通过 spring-boot-starter-oauth2-client 依赖关系使用 keycloak 保护了我的网关。 我使用 TokenRelay 过滤器将承载附加到我的代理请求。 我基本上关注了这个博客https://blog.jdriven.com/2019/11/spring-cloud-gateway-with-openid-connect-and-token-relay/
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8090/auth/realms/testrealm
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
spring.security.oauth2.client.registration.keycloak.client-id=yyy
spring.security.oauth2.client.registration.keycloak.client-secret=xxx
spring.cloud.gateway.default-filters[0]=TokenRelay
现在我正在路由任何与 /ping 匹配的请求到我的 Ping 微服务。
spring.cloud.gateway.routes[0].id=some-id
spring.cloud.gateway.routes[0].uri=http://localhost:8079
spring.cloud.gateway.routes[0].predicates[0]=Path=/ping
Ping 微服务是一个 spring-boot-starter-oauth2-resource-server,并如此配置,并且只公开一个测试端点 /ping。
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8090/auth/realms/testrealm
@Configuration
@EnableWebSecurity
@RestController
public class SecurityConfig extends KeyCloakSecurityConfig {
@GetMapping("/ping")
public String ping() {
return "hello";
}
// ... resource server configuration in KeyCloakSecurityConfig
}
现在我访问我的网关(它在 8765 上运行),通过 http://localhost:8765/ping 我被正确重定向到 keycloak 的登录页面。我使用我的 testuser 登录,并被重定向到我的网关,然后它将我的请求代理到 Ping 微服务。 PingMicroService 再次针对 Keycloak 验证 Accesstoken,我收到了“hello”。
但如果我想用 Postman 等测试我的 API,我会假设我可以在调用我的 /ping 端点之前将 Bearer-Token 添加到 Authorization 标头。 因此,我使用令牌端点 http://localhost:8090/auth/realms/testrealm/protocol/openid-connect/token 获取我的 Accesstoken,并将其复制到我的网关的授权标头中-要求。 但发送后,我得到了登录页面,而不是请求的资源。
当我在授权标头中发送我的有效承载令牌时,为什么我的网关没有通过身份验证?
当我直接调用我的资源服务器时,承载令牌被接受。这是有道理的,因为 api 网关除了将我的访问令牌中继到资源服务器之外没有做任何其他事情,因此他可以进行必要的令牌自省。
经过一些研究,我发现当我通过浏览器进行 api 调用时,Spring 实际上向我发送了一个 SESSION 作为 cookie。 当我将此 SESSION 作为 cookie 复制到邮递员的请求中时,一切正常。 是否有一些关于 Spring 为何使用 SESSION 的文档,或者这基本上是 Auth-Code,因为我们在这里使用授权代码流?
更新: 经过一番研究,我发现我的网关确实已经是无状态的,因此 sessioncookie 不是来自我的网关,而是来自 oauth2-client 依赖项(请参阅 How to make API Gateway Stateless for Authentication/Authorization Process Using Oauth2?)。 所以我用一个简单的 Spring Boot 应用程序重现了我的问题,使用 keycloak 设置 OAuth2login 并公开一个端点 /ping。 见https://github.com/smotastic/spring-oauth2-client-keycloak 如果您使用有效的 Bearer Access-Token 调用 /ping,应用程序会将您重定向到 keycloak 的登录页面,而不接受该令牌,因为它需要一个有效的 SESSION-Cookie
解决方法
所以对于任何有类似问题的人。 问题出在 spring-boot-starter-oauth2-client 依赖项中。 通过从授权服务器发回 SESSION-Cookie 而不是访问令牌,这使我的网关有状态。
不幸的是我不能使用 Keycloak (https://www.keycloak.org/docs/latest/securing_apps/#_spring_boot_adapter) 提供的官方 Spring-Boot-Adapter 因为这个 Adapter 有一些 web 依赖,并且因为 spring-cloud-gateway 是建立在 webflux 上的,所以 web keycloak 需要的依赖不能一起使用。
我的解决方案是,不再使用 spring-cloud-gateway,而是使用 spring-cloud-starter-netflix-zuul 网关。 这是建立在网络上,而不是在 webflux 上,所以我可以通过 keycloak 使用官方的 Spring-Boot-Adapter。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
Zuul 也发送一个 JSessionId,但仍然接受请求头中的 accesstoken(如果设置)作为有效授权。
注意 Zuul 被置于维护模式 (https://cloud.spring.io/spring-cloud-netflix/multi/multi__modules_in_maintenance_mode.html)。 所以它不会收到任何新功能,建议使用 spring-cloud-gateway 代替。 但对于我的用例来说,这是不可行的,所以我将来可能会回到这个网关,如果 oauth2-client 发布更新,我也可以在那里进行无状态身份验证。
这是一个示例存储库,它使用 zuul-gateway,由使用官方 Spring-Boot-Keycloak-Adapter 的 keycloak 保护
https://github.com/smo-snippets/spring-cloud-microservices/tree/master/api-gateway
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。