💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
### 同源策略 > 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制 同源策略主要有两方面的限制: * 接口请求 * DOM操作 为什么呢? 限制接口请求的目的是为了防止CSRF攻击,简而言之,就是在用户在正常网页登录状态下,访问了其他攻击性的网页,则导致攻击性网页模拟用户操作行为。 限制DOM操作的目的是为了防止钓鱼网站的攻击; ### JSONP解决跨域问题 首先,准备两个环境: * 一个是前端环境,通过http-server -p 9100 启动一个html文件,http://localhost:9100 * 服务器环境,通过node+express启动一个小型后端服务,http://localhost:9000 前端代码 ```html <script type='text/javascript'> window.jsonpCb = function (res,data) { console.log(res) console.log(data) } </script> <script src='http://localhost:9000/api/jsonp?msg=helloJsonp&cb=jsonpCb' type='text/javascript'></script> ``` node服务 ```js app.get('/api/jsonp', (req, res) => { // 这里实际返回的是前端的函数方法自执行 res.send(`${req.query.cb}('${req.query.msg}',${Date.now()})`) }) ``` 当然 我们也可以直接使用jquery的ajax中jsonp的方式,但是目前就jquery而言不支持post请求,即使type改为POST也会自动转为GET: ```js // 注意这里指定的回调函数必须属于window对象 function jsonpCb(res, data) { console.info("回调occurs"); console.log(res) console.log(data) } $(function () { $.ajax({ url: 'http://localhost:9000/api/jsonp', type: 'GET', dataType: 'jsonp', jsonpCallback: "jsonpCb", data: 'msg=helloJsonp', success: function (res) { console.log(res) } }) }); ``` 那么如何使用jsonp支持post提交呢,我们使用iframe+form的方式来解决: 这里我们简单点,首先创建一个空的iframe和一个表单: ```html <iframe src="" name="crossIframe" id="crossIframe" style="display: none;"> </iframe> <form action="http://localhost:9000/api/post/jsonp" id="crossForm"> <input type="text" name="age" value="100"> <input type="text" name="name" value="zhangsan"> </form> ``` 然后我们可直接来提交form表单: ```js $(function () { let iframe = document.getElementById('crossIframe') let form = document.getElementById('crossForm') form.method = 'POST' form.target = iframe.name form.submit() }); ``` 服务端我们使用body-parser的中间件: ```js var bodyParser = require('body-parser') app.use(bodyParser.json()) app.use( bodyParser.urlencoded({ extended: true }) ) app.post('/api/post/jsonp', (req, res) => { console.log(req.body) res.send({ success: true, message: '数据提交成功', data: req.body //这里把请求数据再传回去 }) }) ``` ### CORS > 这是W3C官方的标准了,Cross-origin resource sharing 跨域资源共享,是处理跨域问题的标准处理方法。 这里我们要引入一个概念,简单请求和非简单请求: > 简单请求:使用设定的请求方式请求数据 > 非简单请求:使用设定的请求方式请求数据之前,先发送一个OPTIONS请求,看服务端是否允许客户端发送非简单请求. 只有"预检"通过后才会再发送一次请求用于数据传输 区别(只有同时满足以下两个条件时,才是简单请求,否则为非简单请求): * 请求方式:HEAD,GET,POST * 请求信息: * Accept * Accept-Language * Content-Language * Last-Event-ID * Content-Type 对应的值是以下三个中的任意一个 application/x-www-form-urlencoded multipart/form-data text/plain >[info] 虽然可以通过设置响应头和响应方式等支持非简单请求,但是不到万不得已的情况,不能允许客户端发送非简单请求. 因为非简单请求会使服务器比简单请求的多一倍的压力. 不做过度解读吧,但还是把两种方式列出来: - 简单请求 服务端,开启Access-Control-Allow-Origin ```js app.all('/cors', function(req, res, next) { res.header('Access-Control-Allow-Origin', '*') res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS') res.header('Access-Control-Allow-Headers', 'X-Requested-With') res.header('Access-Control-Allow-Headers', 'Content-Type') next() }) // 客户端,直接发请求即可 $.ajax({ url: 'http://localhost:9000/cors', type: 'GET', data: 'msg=she', success: function (res) { console.log(res) } }) ``` ![](images/screenshot_1532069140030.png) - 非简单请求 ```js app.all('/cors', function(req, res, next) { res.header('Access-Control-Allow-Origin', 'http://localhost:9100') res.header('Access-Control-Allow-Credentials', true) // 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight) // 这种情况下除了设置origin,还需要设置Access-Control-Request-Method以及Access-Control-Request-Headers res.header('Access-Control-Request-Method', 'PUT,POST,GET,DELETE,OPTIONS') res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, t') next() }) // 客户端,直接发请求即可 $.ajax({ url: 'http://localhost:9000/cors', type: 'GET', data: 'msg=she', credentials: 'include', // 这里添加额外的headers来触发非简单请求 headers: { 't': 'extra headers' }, success: function (res) { console.log(res) } }) ``` ![](images/screenshot_1532070331860.png) ![](images/screenshot_1532070349530.png) ### 代理 代理的方式就不多讲了,一般都是基于Nginx的反向代理,下面给个简单的配置吧: ```js server { listen 9000; server_name localhost; location / { root index.htm; } location /api { # 这里讲接口转发到10000端口 proxy_pass http://localhost:1000; } } ``` 以上jsonp,cors,代理三种方式就是常见的跨域接口的访问方式。下面讲下DOM的操作方式。 ### postMessage 这里不劳烦服务端,我们使用http-server 启动两个端口的html,分别是: - http://localhost:5000 页面A - http://localhost:7000 页面B 开始之前,我们先了解下`window.postMessage`语法: ```js window.postMessage(message,targetOrigin,[transfer]) ``` A页面代码: ```html <iframe src="http://localhost:7000" name="crossDomainIframe" frameborder="0"></iframe> <script> $(function () { let iframe = window.frames['crossDomainIframe'] $('#sendButton').on('click', e => { iframe.postMessage({ href: location.href, data: 250 }, 'http://localhost:7000') }) window.addEventListener('message', (e) => { // 这里一定要对来源做校验 if (e.origin === 'http://localhost:7000') { console.log(e.data) } }) }) </script> ``` B页面: ```js window.addEventListener('message', (e) => { console.log(e.data) // 这里一定要对来源做校验 if (e.origin === 'http://localhost:5000') { // e.source可以是回信的对象,其实就是http://localhost:7000窗口对象(window)的引用 // e.origin可以作为targetOrigin e.source.postMessage({ href: location.href, data: e.data.data * 2 }, e.origin); } }) ``` ### document.domain 这种方式只适合主域名相同,但子域名不同的iframe跨域