搬砖小抄

spring cloud gateway 配置跨域(CORS)解决swagger 认证失败问题

字数统计: 1.2k阅读时长: 4 min
2020/06/11 Share

在部署web应用时,常常会遇到跨域的问题,而spring cloud gateway作为一个API网关,本身是支持跨域的。这里暂不讨论应不应该开启跨域,只是总结一下:

  • 如何开启跨域
  • 解决的实际问题

以下这段配置,放到spring cloud gateway里面,即可开启跨域,放行任何跨域请求

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowCredentials: true
exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"
allowedHeaders: "*"
allowedOrigins: "*"
allowedMethods: "*"

不要完全照抄这段代码,搞到你的生产环境里面去,原因请阅读相关文档,先了解跨域资源共享(CORS) 。

什么是跨域资源共享(CORS)

MDN 上有关于CORS 的介绍,地址:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS,这里只放一张图:

解决的实际问题

这里以Pigx(一个微服务框架)Swagger聚合文档的登录验证为例,介绍以下跨域问题出在哪里,以及如何解决。

Pigx是商业级框架,他有一个对应的开源版本:pig 也是相同的技术栈,理论上没有区别。

Swagger 聚合文档

在Pigx微服务框架中,为了统一管理API文档,会将各个微服务模块的API文档聚合到API 网关上,这样就可以通过 http://{GATEWAY-HOST}/swagger-ui.html 来访问。聚合解决了文档分散不好管理和使用的问题。

Swagger 登录认证

使用Swagger不仅仅是为了看接口文档,也是后端开发人员的调试和测试利器,然而,后端的接口一般都是受安全框架保护的,并非裸奔,不能直接调,需要有认证凭据才能具备接口调用的权限,而认证凭据是通过登录操作获取的。也就是说,Swagger得具备一种获取认证凭据的能力,其中一种就是Oauth,这需要配置一个OAuth服务的接口地址:

1
2
3
4
swagger:
authorization:
token-url-list:
- http://pigx-gateway:9999/auth/oauth/token

于是,你通过 http://pigx-gateway:9999/swagger-ui.html 打开swagger界面,然后做登录,登录的请求的AJAX接口是http://pigx-gateway:9999/auth/oauth/token,成功拿到登录凭据。这个玩法要求你在本地配置host映射,才能满足同源策略,如果想直接使用IP,那就得改配置,实在繁琐。总之,你的swagger页面在浏览器地址栏中的地址和请求token的地址必须满足同源策略

如果没有满足同源策略,比如pigx-gateway的真实IP是192.168.0.181,你直接通过IP打开swagger页面,然后登录就会以为跨域而失败。

分析网络请求,因为触发跨域,浏览器发起了CORS预检请求,然而服务端不支持,返回了错误。

允许部分跨域请求

原有方案的痛点在于:

  • 必须配置HOST映射,让后通过主机名访问swagger-ui
  • 实际开发过程中,会有多种部署环境,切换HOST虽然不麻烦,但是也是一件繁琐的事情。

实际上,在开发环境中,安全性也不那么重要,在微服务架构中,认证服务器有时候也会作为边缘网络服务暴露在公网。所以允许认证服务器的请求跨域也不是不可用,至少在开发环境是不存在的。

解决办法

在网关加上下面的配置,允许认证服务器的接口跨域,然后重启网关。

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/auth/**]':
allowCredentials: true
exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"
allowedHeaders: "*"
allowedOrigins: "*"
allowedMethods: "*"

这段配置也不是很严谨,开发环境用就可以了

效果测试

不满足同源策略,触发跨域,浏览器发起了CORS预检请求,服务端支持,返回了预检响应。

浏览器检查预检响应后,发起了真正的请求

登录成功

参考资料

2021-02-24 更新

今天pig群里面有小伙伴反应,之前的配置方法不行了,怀疑是pig升级spring cloud 大版本导致的,我测试了一下,仍然有效,测试配置如下

依赖信息(PIG 3.0.5)

1
2
3
4
5
<properties>
<spring-boot.version>2.4.3</spring-boot.version>
<spring-cloud.version>2020.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
</properties>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
cloud:
gateway:
# default-filters:
# - DedupeResponseHeader=Access-Control-Allow-Origin
globalcors:
# add-to-simple-url-handler-mapping: true
corsConfigurations:
'[/auth/**]':
# allowed-origins: "*"
allowedOriginPatterns: "*"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"

注意

  • allowed-origins不再支持通配符,应该通过allowedOriginPatterns来设置。
  • DedupeResponseHeader=Access-Control-Allow-Origin 用于head去重,如果你发现网关返回的CORS头里面head值重复,就需要配置,否则不需要。

效果

CATALOG
  1. 1. 什么是跨域资源共享(CORS)
  2. 2. 解决的实际问题
    1. 2.1. Swagger 聚合文档
    2. 2.2. Swagger 登录认证
    3. 2.3. 允许部分跨域请求
    4. 2.4. 解决办法
    5. 2.5. 效果测试
  3. 3. 参考资料
  4. 4. 2021-02-24 更新