前言
同源策略是浏览器安全策略的核心,非同源不得交互,但是有时候一家公司有自己的不同业务,不同的子域名,或者说有合作方,不同的域名端口需要交互数据呢。这就是跨域的需求。
跨域目前一般常见两种方式:Jsonp、CORS、代理。
JSONP
js在script标签中是不受同源策略限制的,我们也经常能看到<script src="http://xxx.xxx.xx.x/xxx.js">
,当然img也不受限制,但是img不太好做后续数据的处理。
jsonp的核心是在前端动态创建一个script标签。并设置src属性指向跨域的服务器。服务器返回包含json数据的javascript函数。前端接受到对应函数和对应的数据,直接执行处理就拿到了我们想调用接口拿到的数据。
demo
我们实现一个demo,后端用我比较熟悉的Flask
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/jsonp') def jsonp_demo(): callback = request.args.get('callback', 'callbackFunction') data = {'user': 'Alice', 'id': 123} response = f"{callback}({data})" return response
if __name__ == '__main__': app.run(debug=True)
|
如果有浏览器访问/jsonp接口就会获取回调函数的名称,然后把这个接口需要传递的数据封装进去。然后前端代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <!DOCTYPE html> <html> <head> <title>JSONP Demo</title> <script> function handleJSONPResponse(data) { console.log('Received data:', data); } </script> <script> // 动态添加<script>标签进行跨域请求 function requestJSONP(url, callbackName) { var scriptTag = document.createElement('script'); scriptTag.src = url + '?callback=' + callbackName; document.head.appendChild(scriptTag); } requestJSONP('http://localhost:5000/jsonp', 'handleJSONPResponse'); </script> </head> <body> <h1>JSONP跨域请求示例</h1> </body> </html>
|
前端请求http://localhost:5000/jsonp接口,并获取了数据打印。如果不使用jsonp,那么直接在当前域下请求别的域的接口是不合法的。所以jsonp解决了跨域问题。
优点:简单高效兼容性好
缺点:只支持GET请求,还有xss漏洞的奉献。(src的低危的主要贡献漏洞😂)
CORS跨域
有些人看完上面的jsonp跨域可能想,为啥不能script标签里面执行AJAX的动态请求呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"><title>AJAX Request to Baidu</title> <script> document.addEventListener('DOMContentLoaded', function() { var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://www.baidu.com', true); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log(xhr.responseText); } else { console.error('AJAX Request to Baidu failed'); } } }; xhr.send(); }); </script> </head> <body> </body> </html>
|
可以看到仍然被同源策略限制。
怎么解决呢?这里就是CORS了。
1、当浏览器发起跨域请求,它会先发送一个“预检(Preflight)”请求,该请求采用OPTIONS
方法,并携带诸如Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
之类的特定头信息,询问服务器是否愿意接受跨源请求。
2、服务器的回应是关键所在。如果服务器准许,它会回复一个包含Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等头信息的“预检响应”,明确告诉浏览器哪些跨域行为是被许可的。
3、一旦服务器的“预检响应”亮绿灯,浏览器随即会发送正式的请求。服务器此时通过检查请求头的Origin
字段,决定是否允许访问资源,并在响应头中包含Access-Control-Allow-Origin
字段,列明哪些域名可以接收响应数据。
CORS不仅仅是关于XMLHttpRequest请求的,它还涵盖了其他安全机制,包括对cookies和认证的处理。大多数现代浏览器都已支持CORS,但一些旧版的浏览器,像是IE10及其以下版本,则不支持这项功能。
代理实现跨域
我们可以实现一个代理服务器来实现跨域,也就是A和B域之间有一个代理服务器。平常我们写python代码用requets请求百度,从来没有人拦截我们对吧,因为我们不是在浏览器操作,那我们可以请求一个我们自身的接口,这个接口从代码层再去另一个服务器拿数据再返回给前端,那这个接口就是一个代理。
通过实现代理来实现跨域也是一种方法,但是好像不太多见。
服务端代理代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const express = require('express'); const fetch = require('node-fetch');
const app = express(); const PORT = 3000;
app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); next(); });
app.get('/proxy', async (req, res) => { try { const response = await fetch('http://www.baidu.com'); const data = await response.text(); res.send(data); } catch (error) { console.error('Error making proxy request:', error); res.status(500).send('An error occurred while making proxy request'); } });
app.listen(PORT, () => { console.log(`Proxy server is running on http://localhost:${PORT}`); });
|
前端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AJAX Proxy Demo</title> <script> function makeRequest() { var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3000/proxy', true);
xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log(xhr.responseText); document.getElementById('response').innerHTML = xhr.responseText; } else { console.error('Proxy request failed.'); } } };
xhr.send(); } </script> </head> <body> <h1>AJAX Proxy Demo</h1> <button onclick="makeRequest()">Request Baidu through Proxy</button> <div id="response"></div> </body> </html>
|
备注
不太明白一些面试总是会问的特别细,比如一些CORS字段什么的,CSP的一些字段等等,感觉太多啦 有时候确实记不住啊,看见知道是什么意思还不行吗?有点废脑子,有时候知道已经理解啦,但是还是记不住那些参数。