1.什么是跨域问题?
前台调用后台接口的时候不是同一个域的时候就会存在这个问题。
现象:
和普通不跨域请求的区别,多了一个origin:
2.产生跨域问题的原因?
- 浏览器的限制。出于安全的考虑,多管闲事
- 跨域(比如:端口号不一致)
- 发送的是XHR(XMLHttpRequest)请求(重点原因)
3.解决思路
- 让浏览器不限制。禁止检测,指定参数(客户端改动)
- 不发送xhr请求,发送jsonp请求。只能发送get请求
- 修改服务端代码 加header,或者修改nginx
解决办法:
1.客户端浏览器使用命令行参数命令(手动百度) 没有使用价值
2. 使用jsonp
jsonp的实现原理 :
jsonp发送请求的时候有个callback参数。相当于构造了一个callback方法,而请求的返回值就是其中的参数
type是不同的 jsonp使用的Type是script浏览器不会校验,xhr请求会校验如下图
jsonp返回的是script,而xhr返回的是json
使用jsonp时需要修改后端代码:
/** * Created by GAOMINGQIAN on 2018/4/15. * 支持jsonp的Advice */ @ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } }
3.filter解决方案(主要cookie问题处理)
当浏览器发现请求是跨域请求时,会在请求头上增加一个Origin字段,当前跨域主键的域 。在响应头上增加字段
/** * Created by GAOMINGQIAN on 2018/4/15. * 过滤器 */ public class CrosFilter implements Filter { /** * 在doFilter里增加响应头 * @param servletRequest * @param servletResponse * @param filterChain * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response=(HttpServletResponse)servletResponse; //增加响应的域 为*时,不能满足带cookie的调用 HttpServletRequest request=(HttpServletRequest) servletRequest; if(!StringUtils.isEmpty(request.getHeader("Origin"))){ response.addHeader("Access-Control-Allow-Origin",request.getHeader("Origin")); } //自定义头的跨域访问 String headers=request.getHeader("Access-Control-Request-Headers"); if(!StringUtils.isEmpty(headers)){ response.addHeader("Access-Control-Request-Headers",request.getHeader(headers)); } //增加指定允许的方法 response.addHeader("Access-Control-Allow-Methods","*"); //带cookie时得跨域 response.addHeader("Access-Control-Allow-Credentials","true"); //缓存预解命令 提高效率 response.addHeader("Access-Control-Max-Age","3600"); filterChain.doFilter(servletRequest,response); } }
//注册一个Filter的bean @Bean public FilterRegistrationBean registerBean(){ FilterRegistrationBean bean=new FilterRegistrationBean(); //让所有请求都拦截 bean.addUrlPatterns("/*"); //设置filter实例 bean.setFilter(new CrosFilter()); return bean; }
4.Spring框架中的解决方案
在controller上加@CrossOrigin注解即可
5.使用nginx配置
服务方配置
server{
listen 80;
server_name xx.com
location /{
proxy_pass http://localhost: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;
add_header Access-Control-Request-Headers
$access_control_Request_headers;
if ($request_method=OPTIONS){
return 200;
}
}
}
或者前端方配置反向代理
server{
listen 80;
server_name xx.com
location /{
proxy_pass http://localhost:8081/;
}
location /client_server{
proxy_pass http://localhost:8080/test/;
}
}
扩展:
简单请求和非简单请求
简单请求浏览器会先执行后判断。非简单请求会先判断后执行(先发一个预解命令)
工作中比较常见的简单请求方法为:GET HEAD POST
请求header里面无自定义的头
Content-Type为以下几种:
text/plain
multipart/form-data
application/x-www-form-urlencoded
非简单请求方法为: put delete方法的ajax请求
发送json格式的ajax请求
带自定义头的ajax请求
非简单请求会发出一次OPTIONS预检命令
我们可以缓存预解命令提高效率
在返回的头上加 response.addHeader("Access-Control-Max-Age","3600");