一、什么是跨域问题
1.同源策略
同源策略:是指协议,域名,端口都要相同;
2.跨域
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
3.跨域问题
当我们访问网页服务时,针对一个网站,有
- 同一协议,如http,https
- 同一ip地址,如127.0.0.1
- 同一端口,如8090
当以上三个条件的任意一个发生不同时,就会发生跨域问题
二、解决思路
1. 前端解决方案
- 使用JSONP方式实现跨域调用
- 使用NodeJS服务器作为服务代理,前端发起请求到服务器,服务器代理后转发请求到后端服务器。
对于JSONP,有以下优缺点
优点:
- 兼容性比较好,可用于解决主流浏览器的跨域数据访问的问题;
- 不受到同源策略的限制,在请求完毕后可以通过调用 callback 的方式回传结果。
- 数据的格式没有发生很大变化
缺点:
- 仅支持get请求;
- 具有局限性,不安全,可能会受到XSS攻击;
- 只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 javascript 调用的问题。
- jsonp在调用失败的时候不会返回各种HTTP状态码。
2.后端解决方案
- nginx反向代理解决跨域
- 服务端设置Response Header(响应头部)的Access-Control-Allow-Origin
- 在需要跨域访问的类和方法中设置允许跨域访问(如Spring中使用@CrossOrigin注解);
- 继承使用Spring Web的CorsFilter(适用于Spring MVC、Spring Boot)
- 实现WebMvcConfigurer接口(适用于Spring Boot)
三、实际案例
最近当我在把玩Halo博客的时候,由于官方音乐API维护,于是我切换到自建第三方API使用,但这时浏览器报出跨域错误,且音乐模块无法正常使用。
由于我对于Halo项目的架构并不了解,同时第三方API我也没有能力去修改后端代码,所以想到了使用Nginx代理API服务器来解决跨域问题
经过网上搜索资料之后,得到以下具体实现
① 单域名跨域请求
server {
listen 80;
server_name *.51fugj.com;
add_header Access-Control-Allow-Origin 'https://docs.guance.com';
add_header Access-Control-Allow-Headers Origin,Accept,Authorization,platformId,ut,DNT,X-CustomHeader,Keep- Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header Access-Control-Allow-Credentials true;
location ^~/ {
index index.html;
proxy_pass http://frontier.egeo.com/appUnion/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
break;
}
}
② 多域名跨域请求
map $http_origin $corsHost {
default 0;
"~http://boss.guance.com" http://boss.guance.com;
"~http://auth.guance.com" http://auth.guance.com;
}
server {
listen 80;
server_name www.jiagouyun.com;
location /
{
if ($request_method = 'OPTIONS') {
return 204;
}
root /usr/share/nginx/html;
index index.html index.htm;
add_header 'Access-Control-Allow-Origin' $corsHost;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
}
}
server {
listen 80;
server_name www.jiagouyun.com;
location /
{
root /usr/share/nginx/html;
index index.html index.htm;
set $cors '';
if ($http_origin ~* 'https?://(localhost|boss\.guance.com\.com|auth\.guance\.com)') {
set $cors 'true';
}
if ($cors = 'true') {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With';
}
if ($request_method = 'OPTIONS') {
return 204;
}
}
}
server {
listen 80;
server_name www.jiagouyun.com;
location /
{
root /usr/share/nginx/html;
index index.html index.htm;
if ( $http_origin ~ http://(.*).guance.com){
set $allow_url $http_origin;
}
#CORS(Cross Orign Resource-Sharing)跨域控制配置
#是否允许请求带有验证信息
add_header 'Access-Control-Allow-Credentials' 'true';
#允许跨域访问的域名,可以是一个域的列表,也可以是通配符*
add_header 'Access-Control-Allow-Origin' "$allow_url";
#允许脚本访问的返回头
add_header 'Access-Control-Allow-Headers' 'x-requested-with,content-type,Cache-Control,Pragma,Date,x-timestamp';
#允许使用的请求方法,以逗号隔开
add_header 'Access-Control-Allow-Methods' 'POST,GET,OPTIONS,PUT,DELETE';
#允许自定义的头部,以逗号隔开,大小写不敏感
add_header 'Access-Control-Expose-Headers' 'WWW-Authenticate,Server-Authorization';
#P3P支持跨域cookie操作
add_header P3P 'policyref="/w3c/p3p.xml", CP="NOI DSP PSAa OUR BUS IND ONL UNI COM NAV INT LOC"';
}
}
③ 按请求方法区分跨域
server
{
listen 80;
server_name www.jiagouyun.com;
root root /usr/share/nginx/html;
location /
{
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
add_header 'Access-Control-Max-Age' 864000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
}
}