跨域问题
- 什么是跨域问题
- 如何解决跨域问题
- JSONP
- CORS方式解决跨域
- 使用 Nginx 反向代理
- 使用 WebSocket
- 跨源请求是否能携带Cookie
什么是跨域问题
- 跨域问题指的是不同站点之间,使用 ajax 无法相互调用的问题。跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据.
- 同源策略是:拥有相同的协议、域名、端口号的网址间才可以相互访问资源。
- 缺少同源策略,浏览器容易收到XSS、CSRF等攻击。
- 跨域并不是客户端请求发不出去,而是客户端请求发出去了,服务器也正常返回结果了,但是结果被浏览器拦截了。
跨域的三种情况:
- 协议不同,如 http 和 https.
- 域名不同.
- 端口不同.
如何解决跨域问题
JSONP
- JSONP(JSON with Padding)通过没有同源策略限制的script标签发起一个请求(将回调函数的名称放到这个请求的query参数里),然后服务端返回这个回调函数的执行,并将需要响应的数据放到回调函数的参数里,前端的script标签请求到这个执行的回调函数后会立马执行,于是就拿到了执行的响应数据。
前端代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
</head>
<body>
<h2>验证使用JSONP方式发送跨域请求</h2>
<div>
<input type="button" value="发送请求" onclick="ajaxJsonp()">
</div>
</body>
<script type="text/javascript">
// 前端通过动态创建javascript标签的方式发送请求
function ajaxJsonp() {
//将回调函数的名称放到这个请求的query参数里
var url = "http://localhost:8081/jsonp?callback=jsonpcall";
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = url;
document.body.appendChild(script);
}
// jsonp返回数据时调用的函数,数据以参数形式传递
function jsonpcall(data) {
console.log("do response jsonp data");
console.log(data);
}
</script>
</html>
后端代码
/**
* 使用JSONP方式处理跨域GET请求
* @param req
* @param resp
* @param callback 回调函数名称
* @return
*/
@RequestMapping(value = "/jsonp", method = RequestMethod.GET)
@ResponseBody
public Object testAjaxJsonp(HttpServletRequest req, HttpServletResponse resp,
@RequestParam("callback") String callback) {
JSONObject json = new JSONObject();
json.put("name", "jsonp");
json.put("id", "");
// 将数据作为函数的参数返回给浏览器,如: jsonpcall({"name":"jsonp","id":""})
return new StringBuffer().append(callback).append("(").append(json).append(")");
}
优缺点
- jsonp的优点就是兼容性好,可以解决主流浏览器的跨域问题,缺点是仅支持GET请求,不安全,可能遭受xss攻击
CORS方式解决跨域
- cors是跨域资源共享,是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin(域,协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。
- 前端浏览器在IE9以上,后端在响应报头添加Access-Control-Allow-Origin标签,从而允许指定域的站点访问当前域上的资源。
- 不过CORS默认只支持GET/POST这两种http请求类型,如果要开启PUT/DELETE之类的方式,需要在服务端在添加一个"Access-Control-Allow-Methods"报头标签
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("origin"));
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
优缺点
- CORS支持所有类型的HTTP请求,功能完善。(这点JSONP被玩虐,但大部分情况下GET已经能满足需求了)但IE10以下不支持CORS
使用 Nginx 反向代理
- 由于同源策略仅存在于浏览器之间,服务器之间的交互不需要遵循同源策略。
- 所以我们这里可以使用 Nginx 作为 Web 服务器,监听并接收来自外部(其他服务器)请求,将接收到的请求使用和本机相同的域名转发到后端,然后再将响应转发给前端
使用 WebSocket
- HTML5 定义了 Websocket 协议,该协议主要用于服务器和浏览器之间的持久化连接,并且没有同源策略的限制。
- 该方法需要后端提供 Websocket 接口,前端进行接收
- 但如果仅仅是为了实现跨域,不推荐使用 Websocket。
跨源请求是否能携带Cookie
- 默认情况下是跨域是不会携带cookie的,例如:a.com 网站请求 b.com 的接口,这个时候不会把a.com这个域名下的 cookie 携带上。如果需要携带,则需要做一下配置
前端配置
- XMLHttpRequest发请求需要设置
withCredentials=true
- fetch 发请求需要设置
credentials = include
服务器端配置
Access-Control-Allow-Credentials: true