【跨域】jsonp看完这篇文章就够了

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.htmldemo.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内部实现跨域的原理是什么呢?

  1. jsonp使用script标签发送网络请求,这个标签是不受同源策略影响的。
  2. 在url里加入callback参数,指向的是一个全局(如果不是全局的,在页面的script标签中找不到这个函数,也就没法执行)的function。
  3. 最后服务端返回的是一个函数的调用

知道了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

猜你喜欢

转载自blog.csdn.net/colinandroid/article/details/89433240