带有 Spring Auth + 资源服务器的 Spring Gateway - 不断收到 CORS 错误

问题描述 投票:0回答:1
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天了......

angular spring-security spring-cloud-gateway spring-authorization-server
1个回答
0
投票

REST API 是更好配置的“无状态”(无会话)资源服务器。

正在已登录是一种与资源服务器无关的状态。用户会话状态由 OAuth2 客户端负责(在 Spring Security 中,客户端配置为

oauth2Login
)。客户端还负责令牌存储和请求授权(在向资源服务器发送请求时使用承载令牌设置授权标头)。

在您的设置中,网关后面的 REST API 应配置为

oauth2ResourceServer
(禁用会话和 CSRF 保护),网关配置为
oauth2Login
(启用会话和 CSRF 保护),以及
TokenRelay=
过滤器(替换当将请求从前端路由到资源服务器时,会话 cookie 与会话中的访问令牌)。

由于请求是通过前端和网关之间的会话进行授权的,并且 由于会话 cookie 带有

SameSite
标记,因此您需要的不是 CORS 配置,而是 Angular 应用程序和网关之间的反向代理相同的起源。有几种选择:

  • 在开发期间使用 WebPack 代理,否则从网关静态资源提供 Angular 资产
  • 通过网关提供 Angular 资产:为其定义一条路由(会话和
    TokenRelay=
    过滤器在此路由上无用)
  • 在两者之前使用专用的反向代理(例如 Docker 中的 nginx 容器或 k8s 中的入口)

我在 Baeldung 上写了一篇文章,详细介绍了 SPA(Angular、React 和 Vue)的配置、作为 OAuth2 BFF 的 Spring Cloud Gateway(带有 oauth2Login

TokenRelay
 过滤器)和无状态资源服务器( Spring REST API 与 
oauth2ResourceServer
)。

© www.soinside.com 2019 - 2024. All rights reserved.