gateway-url=http://127.0.0.1:9090
authorization-url=http://127.0.0.1:6060
resource-url=http://127.0.0.1:8080
public-spa-url=http://localhost:4200
我有一个 Angular UI,它通过 Spring Gateway 访问资源服务器。 如果资源服务器端点受到保护,那么它将重定向到授权服务器。
我遇到的问题是,当授权服务器授权时,它会重定向到以下URL:
Angular 通过表单发送凭据,将这些凭据放入基本 HTTP 身份验证标头中,然后尝试访问资源服务器中的端点。网关拦截请求(现在不对 Angular Auth 标头执行任何操作),并将其自己的客户端凭据发送到 Auth 服务器,然后服务器创建此 URL
http://127.0.0.1:6060/oauth2/authorize?response_type=code&client_id=api_gateway_client&scope=openid&state=oKol3_tz9yD1-iSQC8p7EUSAIkPaQNKAqx8lYFvxKxk%3D&redirect_uri=http://127.0.0.1:9090/login/oauth2/code/in-house-auth-server&nonce=zh7y02wAKSahkN1AQ0ve5vrQI-QViyelMWqsbbiagRc
所以,我认为,它要求浏览器使用上面的链接返回到授权服务器,但是现在,由于身份验证对象位于安全上下文中,因此它不会重定向到身份验证服务器的登录页面,而是检测到先前授权的用户,并使用保存在短暂的 http 会话中的请求缓存,最终将用户重定向到网关(以 127..9090 开头的嵌入的redirect_url),网关应该拾取随机数令牌,并重定向用户,通过网关到资源服务器(带有访问令牌)......或者,我认为网关只是再次发送其客户端凭据,并使用上面的链接来获取访问令牌(因为我正在使用 auth使用 pkce 进行代码授予 - 因此它与客户端凭据授权授予方法略有不同)
但无论如何,这都不会发生。浏览器此时抱怨:
访问脚本'http://127.0.0.1:6060/oauth2/authorize?response_type=code&client_id=api_gateway_client&scope=openid&state=Pz6Rq1XIcIFT94h7xYoc5hVSfglyTAdQrngnfvLoY6E%3D&redirect_uri=http://127.0.0.1:909 0/登录/oauth2/代码/in- house-auth-server&nonce=biFP__E5abph275Kf7kM5q9HKE6YJg7yErrcAzosvM”(从“http://localhost:9090/polyfills.js”重定向)来自来源“http://localhost:9090”已被 CORS 策略阻止:没有“Access-Control-Allow” -Origin'标头存在于请求的资源上。
该调用的浏览器响应标头如下所示:
GET /oauth2/authorize?response_type=code&client_id=api_gateway_client&scope=openid&state=S_xrtLz45aFQazwTUz22OYmyeq-gwWmTCZNdxof6qLQ%3D&redirect_uri=http://127.0.0.1:9090/login/oauth2/code/in-house-auth-server&nonce=izAjxewKfaMdPx3jWafGnVzp-YO18iJUwv6fMXBG2VQ HTTP/1.1
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: 127.0.0.1:6060
Origin: null
Pragma: no-cache
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
我在网关中配置了 CORS,如下所示:
// configure cors
.cors { cors ->
cors.configurationSource {
CorsConfiguration().apply {
// ensure this matches the Angular app URL
allowedOrigins = listOf("http://localhost:4200")
allowedMethods = listOf("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
allowedHeaders = listOf("Content-Type", "Authorization", "X-XSRF-TOKEN")
exposedHeaders = listOf("Content-Type", "Authorization", "X-XSRF-TOKEN")
// required if credentials (cookies, authorization headers) are involved
allowCredentials = true
}
}
}
但我很困惑,我花了过去 7 个小时尝试调试这个。
我什至尝试通过网关路由 ui,通过这样做(并将 CORS 允许的来源更改为 9090,如果有帮助的话):
@Bean
fun routeLocator(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// routing for Resource Server
.route("resource-server") { r ->
r.path("/resource/**")
.filters { f ->
f.prefixPath("/api")
.filter { exchange, chain ->
// Add your custom filter logic here if needed
chain.filter(exchange)
}
.tokenRelay()
// .saveSession()
}
.uri(resourceUrl)
}
// routing for User Interface
.route("public-spa") { r ->
r.path("/ui/**")
.uri(publicSpaUrl)
}
.build()
}
但是我一直遇到同样的错误。
最后,api网关是auth服务器中的注册客户端,我在其中使用auth代码,授予类型
// api-gateway client (gateway client!)
val apiGatewayClient = RegisteredClient
.withId(UUID.randomUUID().toString())
.clientId(apiGatewayClientId)
.clientSecret(apiGatewayClientSecret)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:9090/login/oauth2/code/in-house-auth-server")
.tokenSettings(
TokenSettings.builder()
.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
.accessTokenTimeToLive(Duration.ofMinutes(5))
.refreshTokenTimeToLive(Duration.ofMinutes(30))
.authorizationCodeTimeToLive(Duration.ofMinutes(1))
.reuseRefreshTokens(false)
.build())
.scope(OidcScopes.OPENID) // requests an ID token, which is necessary for any OpenID Connect request.
.scope(OidcScopes.PROFILE) // requests access to the user's profile information (e.g., name, date of birth)
.clientSettings(ClientSettings
.builder().requireAuthorizationConsent(true)
.build())
.build()
最后,我如何验证 Angular 在请求身份验证标头中发送的登录凭据,一直发送到身份验证服务器 - 因为现在,授权是通过网关将其客户端凭据发送到身份验证服务器进行的。
如果我可以在任何地方放置我的所有代码,以便有人可以告诉我哪里出错了,我也可以这样做。我已经被这个问题困扰了5天了......
REST API 是更好配置的“无状态”(无会话)资源服务器。
正在已登录是一种与资源服务器无关的状态。用户会话状态由 OAuth2 客户端负责(在 Spring Security 中,客户端配置为
oauth2Login
)。客户端还负责令牌存储和请求授权(在向资源服务器发送请求时使用承载令牌设置授权标头)。
在您的设置中,网关后面的 REST API 应配置为
oauth2ResourceServer
(禁用会话和 CSRF 保护),网关配置为 oauth2Login
(启用会话和 CSRF 保护),以及 TokenRelay=
过滤器(替换当将请求从前端路由到资源服务器时,会话 cookie 与会话中的访问令牌)。
由于请求是通过前端和网关之间的会话进行授权的,并且 由于会话 cookie 带有
SameSite
标记,因此您需要的不是 CORS 配置,而是 Angular 应用程序和网关之间的反向代理相同的起源。有几种选择:
TokenRelay=
过滤器在此路由上无用)我在 Baeldung 上写了一篇文章,详细介绍了 SPA(Angular、React 和 Vue)的配置、作为 OAuth2 BFF 的 Spring Cloud Gateway(带有 oauth2Login
和
TokenRelay
过滤器)和无状态资源服务器( Spring REST API 与
oauth2ResourceServer
)。