由于浏览器对于 JavaScript 的 同源策略 的限制,导致 A网站 (Ajax请求) 不能通过JS 去 访问B网站的数据 ,于是跨域问题就出现了。
跨域指的是域名、端口、协议的组合不同就是跨域。
举个例子:比如一个黑客程序,他利用IFrame把真正的银行登录页面嵌套到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过JavaScript读取到你的表单中input中的内容,这样用户名、密码就轻松到手啦。
解决跨域的方式有多种,比如 基于JavaScript 的解决方式、 基于Jquery的JSONP 、以及 基于CORS 的方式。
JSONP和CORS的区别之一: JSONP只能解决get方式提交 、 CORS 不仅 支持GET 方式,同时 还 支持POST 提交方式。
我们重点讲解 CORS跨域 方式。
CORS是一个 W3C 标准,全称是“ 跨域资源共享 ”(Cross-origin resource sharing)。
它允许浏览器向跨资源服务器,发出 XMLHttpRequest 请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器 和 服务器同时支持 。目前,所有浏览器都支持该功能, 浏览器 不能低于IE10 。
CORS原理:只需要向响应头header中注入: Access-Control-Allow-Origin ,这样 浏览器检测 到 header 中的: Access-Control-Allow-Origin ,则就可以 跨域操作 了。
浏览器将CORS请求分成两类:简单请求( simple request )和非简单请求( not-so-simple-request )。
凡是 不同时满足 上面 两个条件 ,就 属于非简单请求 。
浏览器对这两种请求的处理,是不一样的。
对于一个简单请求,浏览器直接发出CORS请求,具体来说,就是在头信息之中,加上一个 Origin 字段。
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或者DELETE,或者Content-Type字段的类型是:application/json。
非简单请求 的 CORS请求 ,会 在正式通信 之 前 ,增 加一次HTTP查询请求 ,称为“ 预检 ”请求(preflight)。
浏览器先咨问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
Http请求的方式是put,并发送一个自定义头信息:X-Custom-Header。
浏览器发现,这是一个非简单请求,就自动发出一个“预检”请求,要求服务器确认可以这样请求。下面是这个“预检”请求的HTTP头信息。
“预检”请求用的请求方法是OPTIONS,表示这个请求是用来咨问的。头信息里面,关键字端是Origin,表示请求来自那个源。
除了Origin字段,“预检”请求的头信息包括两个特殊字段。
一旦服务器通过了“预检”请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样了。会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
具体的【SpringMvc 拦截器】参考
package com.cyb.ssm.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; public class MyHandlerIntercepter implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("Origin:"+request.getHeader("Origin")); if (request.getHeader("Origin") != null) { response.setContentType("text/html;charset=UTF-8"); // 允许哪一个URL response.setHeader("Access-Control-Allow-Origin", "*"); // 允许那种请求方法 response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("XDomainRequestAllowed", "1"); System.out.println("正在跨域"); } return true; //True:允许访问;False:不允许访问 } }
拦截器的配置,请参考【SpringMvc 拦截器】
Access-Control-Allow-Credentials 为 True 的时候, Access-Control-Allow-Origin 一定不能设置为“ * ”,否则 报错 。
如果 有多个拦截器 ,一定要把 处理跨域请求 的 拦截器放 到 首位 。
public class AllowOriginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { if (request.getHeader("Origin") != null) { response.setContentType("text/html;charset=UTF-8"); // 允许哪一个URL 访问 request.getHeader("Origin") 根据请求来的url动态允许 response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); // 允许那种请求方法 response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,HEAD"); response.setHeader("Access-Control-Max-Age", "0"); // 允许请求头里的参数列表 response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token"); // 允许对方带cookie访问 response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("XDomainRequestAllowed", "1"); System.out.println("正在跨域"); } return true; } }