一、什么是跨域问题

1.同源策略

同源策略:是指协议,域名,端口都要相同;

2.跨域

跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。

3.跨域问题

当我们访问网页服务时,针对一个网站,有

  1. 同一协议,如http,https
  2. 同一ip地址,如127.0.0.1
  3. 同一端口,如8090

当以上三个条件的任意一个发生不同时,就会发生跨域问题

二、解决思路

1. 前端解决方案

- 使用JSONP方式实现跨域调用
  - 使用NodeJS服务器作为服务代理,前端发起请求到服务器,服务器代理后转发请求到后端服务器。

对于JSONP,有以下优缺点

优点:

  1. 兼容性比较好,可用于解决主流浏览器的跨域数据访问的问题;
  2. 不受到同源策略的限制,在请求完毕后可以通过调用 callback 的方式回传结果。
  3. 数据的格式没有发生很大变化

缺点:

  1. 仅支持get请求;
  2. 具有局限性,不安全,可能会受到XSS攻击;
  3. 只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 javascript 调用的问题。
  4. 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';
        }
    }
}