啥是跨域

跨源资源共享(CORS,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

跨源 HTTP 请求的一个例子:运行在 https://domain-a.com 的 JavaScript 代码使用 XMLHttpRequest 来发起一个到 https://domain-b.com/data.json 的请求。

出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。例如,XMLHttpRequest 和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。
image.png

为什么会跨域

说到跨域不得不谈的就是浏览器的同源策略,跨域也是因为浏览器这个机制引起的,这个机制的存在还是在于安全。

什么是源

Web 内容的源由用于访问它的 URL 的方案 (协议),主机 (域名) 和端口定义。只有当方案,主机和端口都匹配时,两个对象具有相同的起源。

同源不同源一句话就可以判断:就是 url 中 scheme host port 都相同即为同源。

URL 结构

URL 代表着是统一资源定位符(Uniform Resource Locator)。URL 无非就是一个给定的独特资源在 Web 上的地址。

URL 有如下结构组成:

Scheme 或者 Protocol, 常见的就是 http 或者 https
image.png
Domain Name 也叫做 host 域名
image.png

port 端口号
image.png

Parameters 参数
image.png

Anchor 锚点,一般用于定位位置
image.png

同源不同源举例

就是 url 中 scheme host port 都相同即为同源。 下面是几个不同源的例子, 如果访问就会产生跨域问题.

前端域名 后端域名 原因
https://example.com/ http://example.com/api 协议不一样
http://example.com http://api.example.com host 不一样
http://example.com http://example.com:8080 端口不一样

跨域问题的症状

有跨域问题

image.png

正常情况

image.png

后端解决方案

三、后端解决方案
后端框架也很多,实现原理差不多,都是修改下相应头。以常用的 Java SpringCloud 和 nodejs koa 框架为例。

Http 协议 CORS 头
跨域其实也是 http 层面上可以解决的问题,后端解决也是比较简单的,也是项目常见的解决手法。

CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的 HTTP 头组成,这些 HTTP 头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。

同源安全策略 默认阻止“跨域”获取资源。但是 CORS 给了 web 服务器这样的权限,即服务器可以选择,允许跨域请求访问到它们的资源。其实就是一些 Header 头:

1
2
3
4
5
6
7
8
9
Access-Control-Allow-Origin  指示请求的资源能共享给哪些域。
Access-Control-Allow-Credentials 指示当请求的凭证标记为 true 时,是否响应该请求。
Access-Control-Allow-Headers 用在对预请求的响应中,指示实际的请求中可以使用哪些 HTTP 头。
Access-Control-Allow-Methods 指定对预请求的响应中,哪些 HTTP 方法允许访问请求的资源。
Access-Control-Expose-Headers 指示哪些 HTTP 头的名称能在响应中列出。
Access-Control-Max-Age 指示预请求的结果能被缓存多久。
Access-Control-Request-Headers 用于发起一个预请求,告知服务器正式请求会使用那些 HTTP 头。
Access-Control-Request-Method 用于发起一个预请求,告知服务器正式请求会使用哪一种 HTTP 请求方法。
Origin 指示获取资源的请求是从什么域发起的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class GatewayCorsConfiguation {

@Bean
public CorsFilter corsFilter(){
// 初始化cors配置对象
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true); // 允许使用cookie,但是使用cookie是addAllowedOrigin必须是具体的地址,不能是*
// configuration.addAllowedOrigin("*");
configuration.addAllowedOrigin("http://manage.leyou.com");
configuration.addAllowedMethod("*"); //允许的请求方式,get,put,post,delete
configuration.addAllowedHeader("*");//允许的头信息

//初始化cors的源对象配置
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",configuration);

//3.返回新的CorsFilter.
return new CorsFilter(corsConfigurationSource);
}
}

运维解决方案

Nginx ingress 配置

增加注解

1
2
3
4
5
6
nginx.ingress.kubernetes.io/cors-allow-credentials: 'true'
nginx.ingress.kubernetes.io/cors-allow-headers: >-
DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization
nginx.ingress.kubernetes.io/cors-allow-methods: 'PUT, GET, POST, OPTIONS'
nginx.ingress.kubernetes.io/cors-allow-origin: '*'
nginx.ingress.kubernetes.io/enable-cors: 'true'

Nginx 配置

使用 proxy_pass 转发

此方法是将后端地址反代到前端的一个 location, 这样就是同源了.

1
2
3
location /api {
proxy_pass https://b.test.com; # 设置代理服务器的协议和地址
}

使用 Nginx 增加 Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80;
server_name www.example.com;
root /usr/share/nginx/html;

location / {

proxy_pass http://localhost:8188/;
# 设置是否允许 cookie 传输
add_header Access-Control-Allow-Credentials true;
# 允许请求地址跨域 * 做为通配符
add_header Access-Control-Allow-Origin * always;
# 允许跨域的请求方法
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

if ($request_method = 'OPTIONS') {
return 204;
}
}
}

常见问题

后端服务和 Nginx 同时处理了跨域

跨域问题后端和 Nginx 不要同时处理, 不然会报错:
image.png
这个时候删除 Nginx 上的 Access-Control-Allow-Origin 配置即可, 建议后端和 Nginx 不要同时配置跨域.

Header 不在允许的列表中

出现跨域问题的时候, 可以在浏览器的控制台看到详情, 比如下图提示 x-xxx-client-version 这个 header 不在 Access-Control-Allow-Headers 中, 那你再手动加一下就行了.
image.png