一、什么是跨域
1. 同源策略
同源策略是浏览器中的一种安全机制,用于阻止恶意代码窃取数据或者执行一些危险操作,简单来说就是同协议,同域名,同端口,三者缺一不可。
2.跨域的情景
跨域指的是在浏览器发送请求时,请求的服务器URL与当前页面的域名不一致,即不满足同源策略。比如:
前端网址:http://localhost:8000。
服务器URL:http://localhost:8001。
当该端口的网站8000尝试访问服务器端接口时,由于端口不通,就会触发跨域。
3.跨域的原因
跨域问题主要是由浏览器中的安全机制:同源策略(same-origin policy) 引起的,这是浏览器为了用户安全而设计的一个重要机制。能够阻止恶意网站通过JavaScript访问另一个域的敏感数据或执行潜在的恶意操作。
因此当服务器请求和当前域名不同源时,浏览器会拦截客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
4.浏览器原理
每当发起访问时,浏览器在背后都会校验前端应用和服务端是否同源,源是由一系列属性构成的,包括网络传输协议,主机名称,端口等等。比如,
https://www.facebook-clone.com的传输协议是https,主机名称是www.facebook-clone.com
,端口是443(这里被隐藏了,https协议的默认访问端口是443)。
为了验证同源,浏览器会在所有请求中附带一个特殊的请求一起发送给域信息接收服务器。比如有个应用运行在域localhost:3000上,这个特殊请求的格式将像下面这样:
Origin: http://localhost:3000
作为回应,服务器返回的response会附带一个包含键为 Access-Control-Allow-Origin 的 header,用来标示什么样的源可以访问服务器的资源。该键可以对应两种值:
第一种,严格模式,只有一个指定的源可以获得权限访问:
Access-Control-Allow-Origin: http://localhost:3000
第二种,宽松模式,服务端允许任何源访问自己的资源:
Access-Control-Allow-Origin: *
浏览器接收到该响应后,会把前端应用的域和服务器返回的Access-Control-Allow-Origin值比较,如果比较不匹配,那么此次访问就被报错了,并且会返回一个CORS错误的提示。
二、跨域的解决方案
解决跨域问题有多种方法,如JSONP、CORS、Nginx代理等。
1.前后端结合(JsonP)
虽然jsonp也可以实现跨域,但是因为jsonp不支持post请求,应用场景受到很大限制,所以不考虑。
2.后端配置(跨域资源共享(CORS))
跨域资源共享即开放跨域请求,CORS 是w3c标准的方式,服务器默认是不被允许跨域的,可以通过给Nginx服务器配置Access-Control-Allow-Origin *,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。
在实际的浏览器发送请求中,如果浏览器检测到跨域,它会尝试发起一次请求,然后查看返回的内容中,是否一个有允许跨域请求的标记(CORS响应头),如果有正确的标记,那么就不拦截;如果没有标记,浏览器就会阻止这个请求,并报错。
同源策略只存在浏览器端,通过服务器转发请求可以达到跨域请求的目的,但是会增加服务器的负担,且访问速度慢。
3.Nginx配置代理请求的CORS
通过Nginx代理可以将跨域变成同源。首先要明白,是谁把前台向后台的请求拦截下来的?不是后台,而是浏览器。如果要避免跨域,只要让浏览器认为“我正在向同一个域发起请求”,就可以了。
在前端的代码中,可以在服务器的请求URL前加个特殊前缀,比如加一个/api/作为前缀,然后再nginx的配置中识别到/api/这个前缀就转发到服务器实际的地址中。
server {
listen 80;
server_name localhost;
location /api/ {
proxy_pass http://localhost:8001/;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
expires 60s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
一般情况下浏览器访问时是只显示客户端的访问地址,而服务器nginx转发到哪个地址我们是看不到的,这样就很难判断nginx代理是否生效。我们可以在nginx的配置文件上加上以下配置:
add_header backendIP $upstream_addr;
配置更新了之后,需要再重启一下Nginx服务。
nginx -s reload
如果不想加特殊前缀,也有其他的方式可以解决
3.1.history模式
history 模式会有一个问题,就是当页面刷新时,如果没有合适的配置,会出现页面 404 的错误,因此需要额外的配置。
try_files
try_files 解决的是:当 nginx 找不到客户端需要的资源时该怎么办的问题。对于找不到的 url,需要将首页 html 返回, nginx 配置如下:
location / {
try_files $uri $uri/ /index.html;
}
这样虽然解决了页面刷新404的问题,但是如果用post调用后端接口服务,会出现405 not allowed。这是因为 Nginx 会将匹配不上的url 匹配到首页html,但是html是静态资源,Nginx服务器默认post不能访问静态资源,所以会出现 405 not allowed。
error_page
error_page可以将所有POST 405错误发送到命名位置@405 , 配置如下:
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
index index.html index.htm;
error_page 405 =200 @405; #405页面处理
}
location @405 {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ip:端口$request_uri ;
}
配置了之后接口访问正常
3.2 hash模式
hash不论页面的路由路径是什么,实际网络请求的都是主域名或IP:Port,所以并不会出现404问题。所以Nginx 配置并不需要加 try_files,但是路由找不到请求路径的文件,会出现404,所以需要配置 error_page。
location / {
root /usr/share/nginx/html;
index index.html index.htm;
error_page 404 =200 @404; #404页面处理
}
location @404 {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ip:端口$request_uri ;
}
三、可能会出现的问题
1.Nginx报错405 Method Not Allowed
Request URL: http://localhost:8001/api/getinfo
Request Method: POST
Status Code: 405 Not Allowed
Remote Address: localhost:8001
Referrer Policy: strict-origin-when-cross-origin
前置情况:前端解决跨域问题、路由history模式
出现这个原因:正常情况下,接口地址可以直接设置后端接口地址,但是这样会引起浏览器跨域,如果后端没有设置接口允许跨域,那么就需要前端在nginx做一层转发。为了解决浏览器跨域问题,接口地址直接设置的业务系统访问地址,这接口就相当于去请求 /api/getinfo 这个路径下的文件,由于静态资源的,都不允许静态文件响应POST请求,只能通过get方法访问,所以这里就报错405。
解决方法:加前缀转发,或者配置配置405状态转发。
2. Nginx报错404 Not Found
前置情况:前端解决跨域问题、路由hash模式
出现这个原因:跟上面出现的情况一样,都是因为nginx配置问题,也是Nginx把接口请求当做访问静态资源,找不到文件就报错404。
解决方法:加前缀转发,或者配置配置404状态转发。
3. Nginx报错502 Bad Gateway
页面报错显示502 Bad Gateway,查看后端nginx发现并无前端的请求,并且后端服务没有问题。使用proxy_pass 中填写的地址用postman模拟是可以正常访问。
问题的原因是在于 nginx中传递了host,而后端也是nginx反向代理,而此nginx中并无原始请求的host,故报502错误。把Host的设置删掉即可。
proxy_set_header Host $host;
4. 接口返回HTML
这是因为nginx配置导致的
server {
listen 80
server_name localhost;
location / {
root html;
index index.html index.htm;
}
# To allow POST on static pages 允许静态页使用POST方法
error_page 405 =200 $uri;
}
error_page 405 =200 $uri; 是将POST请求转换为GET。而使用此方法会出现返回不一致的问题,如这里应返回json文本,此处却是 html的文本,其原因就是因为请求的方式不一致,因返回格式不一致,导致前端报错。

1531

被折叠的 条评论
为什么被折叠?



