jsonp是一种jQuery提供的跨域解决方案,我们今天来好好讲讲jsonp。
同源策略及限制
所有浏览器都会使用同源策略这个安全策略。
所谓同源,是指协议、域名、端口号都相同。
那么,同源策略就是,一个源只能加载同源的资源。
那么同源策略有什么限制呢?
- Cookie、LocalStorage和indexDB不同源无法读取
- dom不同源无法获取
- ajax不同源请求不能发送
没有同源的危险场景(CSRF攻击)
1.CSRF是什么呢?
跨站请求伪造。CSRF全名是Cross-site request forgery,是一种对网站的恶意利用,CSRF比XSS更具危险性。
2.CSRF攻击的主要目的
是让用户在不知情的情况下攻击自己已登录的一个系统(类似于钓鱼)。
例如,
- 用户当前已经登录了A网站,同时用户又在使用另外一个钓鱼网站。
- 这个网站上面可能因为某个图片吸引你,你去点击一下,此时可能就会触发一个js的点击事件,构造一个A网站的请求。
- 利用当前cookie中的登陆状态,让用户在不知情的情况下,帮你干一些事情。
3.防御CSRF
- 同源检测
在HTTP协议中,每一个异步请求都会携带两个Header,用于标记来源域名(Origin Header和Referer Header)。这两个Header在浏览器发起请求时,大多数情况会自动带上,并且不能由前端自定义内容。 服务器可以通过解析这两个Header中的域名,确定请求的来源域。 - token令牌
CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。
要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。
那么,我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。
跨域的简单原理
我们新建一个web程序,里面包含demo.html
和demo.js
文件如下:
//demo.html
<!DOCTYPE>
<html>
<head>
<title>test</title>
<script type="text/javascript" src="demo.js"></script>
</head>
<body>
</body>
</html>
//demo.js
alert("success");
打开demo.html
会弹出success
对话框。
当然,现在这种情况是同源的,我们来模拟一下非同源情况。
我们再新建一个web程序,把demo.js
放到这个程序里。现在demo.html
中访问demo.js
就变成了下面这样:
<script type="text/javascript" src="http://localhost:xxxx/demo.js"></script>
这个时候,其实就是访问到了非同源的demo.js
。
<script>标签的src属性并不被同源策略所约束,所以可以获取任何服务器上脚本并执行。
jsonp如何处理跨域
$.ajax({
url:"http://api.map.baidu.com/telematics/v3/weather?ak=6tYzTvGZSYB5Oc2YGGOKt8&location=天津&output=json",
type:"get",
dataType:"jsonp",
success:function(data){
console.log(data);
}
})
只需要指定dataType
是jsonp格式就行了。
这只是简单的使用,那么jsonp内部实现跨域的原理是什么呢?
- jsonp使用script标签发送网络请求,这个标签是不受同源策略影响的。
- 在url里加入callback参数,指向的是一个全局(如果不是全局的,在页面的script标签中找不到这个函数,也就没法执行)的function。
- 最后服务端返回的是一个函数的调用。
知道了jsonp的实现原理,我们来手写一个jsonp。
function jsonP({url, data, callbackName}) {
return new Promise((resolve, reject) => {
//1. 对请求进行url编码
function formatParams(data) {
let arr = []
for(let key in data) {
if(data.hasOwnProperty(key)) {
arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
}
}
return arr.join('&')
}
//2. 创建script标签
const script = document.createElement('script')
//3. 设置好回调方法
window.jsonCb = function(res) {
document.body.removeChild(script)
delete window.jsonCb
resolve(res)
}
//4. 请求
script.src = `${url}?${formatParams(data)}&jsonCb=${callbackName}`
//5. 添加到dom结构中
document.body.appendChild(script)
})
}
jsonp需要服务端配合
服务端会读取请求参数中的jsonpCallback
字段,并返回。
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExchangeJsonController {
@RequestMapping("/base/json.do")
public void exchangeJson(HttpServletRequest request,HttpServletResponse response) {
try {
response.setContentType("text/plain");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
Map<String,String> map = new HashMap<String,String>();
map.put("result", "content");
PrintWriter out = response.getWriter();
JSONObject resultJSON = JSONObject.fromObject(map); //根据需要拼装json
String jsonpCallback = request.getParameter("jsonpCallback");//客户端请求参数
out.println(jsonpCallback+"("+resultJSON.toString(1,1)+")");//返回jsonp格式数据
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
跨域通信的几种方式
这里趁热打铁,再介绍几种jsonp之外的跨域通信方式。
- hash
- postMessage
- webSocket
- CORS
参考
文中jsonp实现代码收录在https://github.com/colinNaive/algorithm
https://www.cnblogs.com/soyxiaobi/p/9616011.html
https://www.cnblogs.com/dream0530/p/6179819.html
https://blog.csdn.net/qq_33562825/article/details/60765688
https://segmentfault.com/a/1190000007665361
https://juejin.im/post/5c9c38e2e51d452db7007f66
https://juejin.im/post/5be4f163f265da61483b1b08
https://juejin.im/entry/589921640ce46300560ef894
https://segmentfault.com/a/1190000015597029
https://blog.csdn.net/as645788/article/details/51285688
https://www.cnblogs.com/shytong/p/5308667.html
https://www.cnblogs.com/cxying93/p/6035031.html
https://juejin.im/post/5bc009996fb9a05d0a055192#heading-8