如何让Keycloak支持CORS

2025-03-26T14:55:19+08:00 | 7分钟阅读 | 更新于 2025-03-26T14:55:19+08:00

Macro Zhao

背景

API网关,如Spring Cloud Gateway,通常在微服务架构中使用,以在单个端点上公开多个服务。

通过API网关公开IAM解决方案(如Keycloak)并不少见。

某些端点可能需要CORS支持,这通常在API网关级别进行管理。

Keycloak

根据https://www.keycloak.org/about 的描述,“Keycloak是一个面向现代应用程序和服务的开源身份和访问管理解决方案。它使得应用程序和服务的安全变得简单,几乎不需要编写代码。”

Spring Cloud Gateway

根据https://spring.io/projects/spring-cloud-gateway 的描述,Spring Cloud Gateway“提供了一个库,用于在Spring WebFlux之上构建API网关。Spring Cloud Gateway旨在提供一种简单而有效的方式来路由到API,并为它们提供安全、监控/指标和弹性等跨领域关注点。”

CORS

根据https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS 的描述,“跨源资源共享(CORS)是一种基于HTTP标头的机制,允许服务器指示浏览器允许加载资源的除自身以外的任何源(域名、方案或端口)。”

如何复现问题

要复现问题,您需要以下设置:

  • Keycloak版本12或更高版本(版本12引入了Referrer-Policy响应头,尽管有一个错误报告指出版本11.0.2也存在此问题)
  • 启用了CORS的API网关,如Spring Cloud Gateway

以下步骤将复现问题:

  1. 使用浏览器导航到Keycloak登录页面(我们使用了Firefox和Chrome),例如本地设置的http://localhost:8080/auth/admin/

  2. 在浏览器开发者工具网络标签中观察HTTP响应头“Referrer-Policy: no-referrer”。

  1. 提供有效的登录凭据,并点击登录按钮。

  2. 观察HTTP响应403禁止和浏览器中的空白页面。

  3. 观察HTTP请求头“origin: null”。

如果您无法复现问题,请确保:

  • 浏览器与API网关通信,而不是直接与Keycloak通信。
  • API网关使用非通配符“Access-Control-Allow-Origin”启用CORS,例如使用以下配置属性:
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedOrigins="https://some.domain.org"
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedMethods=GET

解决方案

问题可以通过在API网关中重写Keycloak的HTTP响应头来解决(或者更确切地说,绕过问题),例如,让浏览器接收“Referrer-Policy: same-origin”而不是“Referrer-Policy: no-referrer”。

Spring Cloud Gateway提供了一个方便的RewriteResponseHeaderGatewayFilterFactory来处理这个问题,我们按以下方式设置:

@Bean
public RouteLocator theRoutes(RouteLocatorBuilder builder) {
    return builder.routes()
   	 .route("auth", r ->
   		 r.path("/auth/**")
   		 .filters(f -> f.rewriteResponseHeader("Referrer-Policy", "no-referrer", "same-origin"))
   		 .uri("https://keycloak"))
   	 .build();
}

然后您可以重复以下步骤来观察修复是否有效

  1. 使用浏览器导航到Keycloak登录页面,例如本地设置的http://localhost:8080/auth/admin/

  2. 在浏览器开发者工具网络标签中观察HTTP响应头“Referrer-Policy: same-origin”。

  1. 提供有效的登录凭据,并点击登录按钮。

  2. 观察HTTP响应302找到和浏览器中的目标页面。

  3. 观察HTTP请求头“origin: http://localhost:8080”。

问题与考虑

为什么API网关拒绝HTTP请求?

当“Referrer-Policy”为“no-referrer”时,浏览器会发送HTTP请求头“origin: null”。

每当HTTP请求中存在“origin”头时,API网关会将其视为CORS请求。CORS请求会导致API网关验证源是否在允许的源列表中。对于“null”,这通常不是这种情况(因为不推荐这样做),导致API网关以HTTP 403禁止拒绝请求。

为什么不直接允许null作为CORS源?

(注:此处原文未给出答案,以下为可能的解释)

直接允许null作为CORS源可能会导致安全问题,因为CORS策略的一个重要部分是限制哪些域可以与您的服务进行交互。允许null源可能会导致任何域都可以与您的服务进行交互,这可能会打开跨站点请求伪造(CSRF)攻击的大门。因此,为了保持安全性,通常不建议将null作为允许的CORS源。允许将’null‘作为’Access-Control-Allow-Origin‘是不推荐的,因为这会引入多个安全问题。更多信息请参见https://w3c.github.io/webappsec-cors-for-developers/#avoid-returning-access-control-allow-origin-null

为什么不直接允许所有来源/方法用于CORS?

虽然允许所有来源/方法用于CORS可以防止问题,但它也会引入重大的安全问题。CORS设置应始终尽可能限制性,特别是在涉及敏感数据(如凭据)时。详情请参见https://stackoverflow.com/a/56457665

Keycloak不应该修复这个问题吗?

我们建议Keycloak使’Referrer-Policy‘的值可配置,就像他们允许某些其他头部一样。

以下是在2020年10月为此问题创建的Keycloak错误报告(不是我们创建的),建议正是如此:https://issues.redhat.com/browse/KEYCLOAK-16032

不知何故,Keycloak的开发人员在错误线程中混淆了,并以“’null‘是’origin‘的有效值”(这并不是问题)为由将其标记为“已解释”,而不是实际解决问题(例如,通过使’Referrer-Policy‘可自定义,类似于其他HTTP响应头部值)。

这个解决方案不会降低整体安全性吗?

确实,’no-referrer‘比例如’same-origin‘的’Referrer-Policy‘更为严格,因为它导致浏览器在’Referer‘和’origin‘头部向API网关发送更少的细节。

然而,结合使用CORS启用的API网关,使用’no-referrer‘从系统级别完全破坏了Keycloak,使其完全无用。因此,使用此设置提供的安全性和功能性与关闭Keycloak相同。

为了使这与启用CORS的API网关一起工作,浏览器必须发送更多细节,特别是对于同一来源的请求,为’origin‘头部提供一个适当的值。这可以通过使用不同的’Referrer-Policy‘来实现,例如’same-origin’。

对我们来说,这是一个可以接受的(非常有限的)安全性降低,至少拥有预期的功能;此外,这些细节只影响同一来源,这完全在我们的控制之下。

我可以使用其他Referrer-Policy值吗?

你可以选择一个适合你设置的referrer-policy,没有要求必须使用’same-origin’。例如,典型的浏览器默认值’strict-origin-when-cross-origin‘也可以正常工作。

© 2011 - 2025 Macro Zhao的分享站

关于我

如遇到加载502错误,请尝试刷新😄

Hi,欢迎访问 Macro Zhao 的博客。Macro Zhao(或 Macro)是我在互联网上经常使用的名字。

我是一个热衷于技术探索和分享的IT工程师,在这里我会记录分享一些关于技术、工作和生活上的事情。

我的CSDN博客:
https://macro-zhao.blog.csdn.net/

欢迎你通过评论或者邮件与我交流。
Mail Me

推荐好玩(You'll Like)
  • AI 动·画
    • 这是一款有趣·免费的能让您画的画中的角色动起来的AI工具。
    • 支持几十种动作生成。
我的项目(My Projects)
  • 爱学习网

  • 小乙日语App

    • 这是一个帮助日语学习者学习日语的App。
      (当然初衷也是为了自用😄)
    • 界面干净,简洁,漂亮!
    • 其中包含 N1 + N2 的全部单词和语法。
    • 不需注册,更不需要订阅!完全免费!
  • 小乙日文阅读器

    • 词汇不够?照样能读日语名著!
    • 越读积累越多,积跬步致千里!
    • 哪里不会点哪里!妈妈再也不担心我读不了原版读物了!
赞助我(Sponsor Me)

如果你喜欢我的作品或者发现它们对你有所帮助,可以考虑给我买一杯咖啡 ☕️。这将激励我在未来创作和分享更多的项目和技术。🦾

👉 请我喝一杯咖啡

If you like my works or find them helpful, please consider buying me a cup of coffee ☕️. It inspires me to create and share more projects in the future. 🦾

👉 Buy me a coffee