跨域处理

514 阅读2分钟

为什么会出现跨域:

1.浏览器限制 2.同源策略,协议(http,https),域名(www.xx.com),端口(80) 其中一个条件不一样就会出现跨域 3.XHR(XMLHttpRequest)请求才有的, 如果用其他方式请求则不会出现:如用一个图片加载的方式去加载一个get请求,不会出现跨域错误

<img src="http://localhost:8080/get"/>

解决思路

浏览器:设置浏览器让其允许跨域

找到chrome.exe 文件,使用命令,新打开一个允许跨域的浏览器

 .\chrome --disable-web-security --user-data-dir=g:\temp3

重修请求界面,会发现浏览器已经允许跨域了

本地测试采取这种方式比较方便。

XHR:不用XHR发送请求, 换成JSONP请求

jsonp的跨域原理是浏览器发出的是script请求,script由jquery动态生成 返回类型也变成了 js脚本application/javascript

$.ajax({
	url:"xx.com",
    dataType: "jsonp",
    jsonp:"callback",
    success: function(){}
 })

把请求的dataType换成jsonp, 浏览器发送请求的时候会带一个callback参数,服务器获得了callback参数后就知道这个是个jsonp请求,(这里jsonp参数是默认的callback),然后把本来json的数据,转换成js类型数据,返回回去。

动态生成的script

缺点:需要改动服务器 只支持GET请求,

跨域:被调用方,让服务器接口设置允许跨域,

	调用方,做一个代理,发送请求的时候把指定url转到服务器域名里面

设置服务器端代码:

服务器端代码用springboot写的,修改方式如下

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Demo1Application {

	public static void main(String[] args) {
		SpringApplication.run(Demo1Application.class, args);
	}
	// 在主函数里面添加过滤器
	@Bean
	public FilterRegistrationBean registerFilter() {
		FilterRegistrationBean bean = new FilterRegistrationBean();
		bean.addUrlPatterns("/*");
		bean.setFilter(new CrosFilter());
		return bean;
	}
}

package com.example.demo;
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class CrosFilter implements Filter {

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// TODO Auto-generated method stub
		HttpServletResponse res = (HttpServletResponse) response;
        // 请求的域名匹配任意域名
		res.addHeader("Access-Control-Allow-Origin", "*");
	// 请求方法匹配任意方法
res.addHeader("Access-Control-Allow-Methods", "*");
		chain.doFilter(request, response);
	}

}

简单请求,非简单请求

浏览器在发送跨域请求的时候会根据请求 是否是简单请求来做不同的处理,简单请求就会先处理后判断,非简单请求就会先做预检命令处理后 在把命令发送出去

常见简单请求:

方法为:GET,HEAD,POST
请求header里面,无自定义头
content-type为以下几种:
text/plain,multipart/form-data, application/x-www-form-urlencoded

非简单请求:

put,delete方法的ajax请求
发送json格式的ajax请求
带自定义头的ajax请求

OPTIONS预检命令

缓存预检命令:Access-Control-Max-Age:3600 表示在一个小时内可以缓存预检命令,不需要重复发送

那些条件 "Access-Control-Allow-Origin", "*" 不能跨域

发送请求的时候携带了cookie时, Access-Control-Allow-Origin必须为指定的域名,不能用通配符* 同时 Access-Control-Allow-Credentials:true

$.ajax({
	type:"get",
   url: "xx",
   xhrFields:{
   	widthCredentials: true  // 发送请求的时候携带上cookie
   },
   success: function(){
   }
})

在服务器端可以获取 发送方的域名Origin,然后设置到Access-Control-Allow-Origin 所有域名就都可以跨域了,包括传cookie的请求

HttpServletRequest req = (HttpServletRequest) request;
   	String origin =  req.getHeader("Origin");
   	
   	if(!org.springframework.util.StringUtils.isEmpty(origin)){
   		res.addHeader("Access-Control-Allow-Origin", origin);
   	}

Nginx配置代理服务器

1.修改window的hosts文件,配置一个本地的域名 添加一行127.0.0.1 a.com, 现在访问本地a.com,就可以访问本地127.0.0.1了

然后在nginx根目录创建一个vhost文件夹

然后再nginx.conf里面最后面添加 读取vhost文件里面的所有文件

再vhost里面创建a.com.config

写入如下内容

server{
   listen 80; # 监听端口
   server_name a.com; # 监听域名
   location /{
       proxy_pass http://location:8080/;
       
       add_header Access-control-Allow-Methods *;
       add_header Access-control-Max-Age 3600;
       add_header Access-control-Allow-Credentials true;

       add_header Access-control-Allow-Origin $http_origin; 
       #$http_origin 可以获取到http请求头下面origin的值,然后写回到请求头里面
       add_header Access-Control-Allow-Headers $http_access_control_request_headers;
   	
       #直接处理浏览器的预检命令,
       if ($request_method==OPTIONS){
           return 200;
       }

   }
}

启动nginx 运行nginx -t 测试运行,成功后nginx -s reload 重修载入 请求接口也换成a.com/8080/xx 就可以了

spring框架解决办法

隐藏跨域

本地hosts添加一个abc.com地址

nginx添加一个文件

server{
   listen 80;
   server_name abc.com;
   location /{
       proxy_pass http://localhost:8020/;
   }

   location /ajaxserver{
       proxy_pass http://localhost:8080/test/;
   }
}

配置成功后 nginx -s reload 重新加载后 访问本地abc.com 地址