摘要:跨域的解决方案利用标签不受跨域限制而形成的一种方案。跨域资源共享标准新增了一组首部字段,允许服务器声明哪些源站有权限访问哪些资源。它是基于的全双工通信即服务端和客户端可以双向进行通讯,并且允许跨域通讯。
1.什么是跨域
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。同源指:协议、域名、端口号必须一致。
同源策略控制了不同源之间的交互,例如在使用XMLHttpRequest 或 标签时则会受到同源策略的约束。这些交互通常分为三类:
通常允许跨域写操作(Cross-origin writes)。例如链接(links),重定向以及表单提交。特定少数的HTTP请求需要添加 preflight。
通常允许跨域资源嵌入(Cross-origin embedding)。
通常不允许跨域读操作(Cross-origin reads)。但常可以通过内嵌资源来巧妙的进行读取访问。例如可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或availability of an embedded resource.
下面为允许跨域资源嵌入的示例,即一些不受同源策略影响的标签示例:
标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type 消息头。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。
嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...
@font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
和
2.跨域的解决方案 jsonp利用script标签不受跨域限制而形成的一种方案。
// index.html function jsonp({url, param, cb}){ return new Promise((resolve, reject)=>{ let script = document.createElement("script") window[cb] = function(data){ resolve(data); document.body.removeChild(script) } params = {...params, cb} let arrs = []; for(let key in params){ arrs.push(`${key}=${params[key]}`) } script.src = `${url}?${arrs.join("&")}` document.body.appendChild(script) }) } jsonp({ url: "http://localhost:3000/say", params: {wd: "haoxl"}, cb: "show" }).then(data=>{ console.log(data) })
//server.js let express = require("express") let app = express() app.get("/say", function(req, res){ let {wd,cb} = req.query console.log(wd) res.end(`${cb}("hello")`) }) app.listen(3000)
缺点:只支持get请求,不支持post、put、delete等;不安全,容易受xss攻击。cors
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
Nice to meet you
// server1.js let express = require("express"); let app = express(); app.use(express.static(__dirname)); app.listen(3000)
// server2.js let express = require("express"); let app = express(); let whiteList = ["http://localhost:3000"] app.use(function(req, res, next){ let origin = req.headers.origin; if(whiteList.includes(origin)){ //设置那个源可以访问我,参数为 * 时,允许任何人访问,但是不可以和 cookie 凭证的响应头共同使用 res.setHeader("Access-Control-Allow-Origin", origin); //允许带有name的请求头的可以访问 res.setHeader("Access-Control-Allow-Headers","name"); // 设置哪些请求方法可访问 res.setHeader("Access-Control-Allow-Methods", "PUT"); // 设置带cookie请求时允许访问 res.setHeader("Access-Control-Allow-Credentials", true); // 后台改了前端传的name请示头后,再传回去时浏览器会认为不安全,所以要设置下面这个 res.setHeader("Access-Control-Expose-Headers","name"); // 预检的存活时间-options请示 res.setHeader("Access-Control-Max-Age",3) // 设置当预请求发来请求时,不做任何处理 if(req.method === "OPTIONS"){ res.end();//OPTIONS请示不做任何处理 } } next(); }); app.put("/getData", function(req, res){ console.log(req.headers) res.setHeader("name","hello"); res.end("hello world"); } app.get("/getData", function(){ res.end("Nice to meet you") }) app.use(express.static(__dirname)); app.listen(3000)postMessage
对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后(e.g., 在该方法之后设置的事件、之前设置的timeout 事件,etc.)向目标窗口派发一个MessageEvent消息。 该MessageEvent消息有四个属性需要注意:
message 属性表示该message 的类型;
data 属性为 window.postMessage 的第一个参数;
origin 属性表示调用window.postMessage() 方法时调用页面的当前状态;
source 属性记录调用 window.postMessage() 方法的窗口信息;
targetOrigin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。
案例:a.html 给b.html发消息
// a.html
// b.htmlwindow.name
页面可能会因某些限制而改变他的源。脚本可以将 document.domain 的值设置为其当前域或其当前域的超级域。如果将其设置为其当前域的超级域,则较短的域将用于后续源检查。a和b是同域的http://localhost:3000, c是独立的http://localhost:4000。
a通过iframe引入c,c把值放到window.name,再把它的src指向和a同域的b,然后在iframe所在的窗口中即可取出name的值。
// a.html
// c.html
//server.js let express = require("express") let app = express(); app.use(express.static(__dirname)); app.listen(4000);location.hash
window.location 只读属性,返回一个Location对象,其中包含有关文档当前位置的信息。window.location : 所有字母必须小写!只要赋给 location 对象一个新值,文档就会使用新的 URL 加载,就好像使用修改后的 URL 调用了window.location.assign() 一样。需要注意的是,安全设置,如 CORS(跨域资源共享),可能会限制实际加载新页面。案例:a、b同域,c多带带一个域。a现在想访问c:a通过iframe给c传一个hash值,c收到hash值后再创建一个iframe把值通过hash传递给b,b将hash结果放到a的hash值中。
// a.html
// c.html //接收a传来的hash值 console.log(location.hash) //创建一个iframe,把回复的消息传给b let iframe = document.createElement("iframe"); iframe.src="http://localhost:3000/b.html#idontloveyou"; document.body.appendChild(iframe);
//b.htmlwindow.domain
window.domain:获取/设置当前文档的原始域部分。
案例:解决一级域与二级域之间通信。 模拟时需要创建两个不同域的域名用来测试,打开C:WindowsSystem32driversetc 该路径下找到 hosts 文件,在最下面创建一个一级域名和一个二级域名。改为:
127.0.0.1 www.haoxl.com 127.0.0.1 test.haoxl.com
预设a.html = http://www.haoxl.com,
b.html = http://test.haoxl.com
// a.html
// b.html document.domain = "haoxl.com" var a = "hello world"websocket
WebSocket对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。它是基于TCP的全双工通信,即服务端和客户端可以双向进行通讯,并且允许跨域通讯。基本协议有ws://(非加密)和wss://(加密)
//socket.html let socket = new WebSocket("ws://localhost:3000"); // 给服务器发消息 socket.onopen = function() { socket.send("hello server") } // 接收服务器回复的消息 socket.onmessage = function(e) { console.log(e.data) } // server.js let express = require("express"); let app = express(); let WebSocket = require("ws");//npm i ws // 设置服务器域为3000端口 let wss = new WebSocket.Server({port:3000}); //连接 wss.on("connection", function(ws){ // 接收客户端传来的消息 ws.on("message", function(data){ console.log(data); // 服务端回复消息 ws.send("hello client") }) })Nginx
Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。案例:在nginx根目录下创建json/a.json,里面随便放些内容
// client.html let xhr = new XMLHttpRequest; xhr.open("get", "http://localhost/a.json", true); xhr.onreadystatechange = function() { if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304 ){ console.log(xhr.response); } } }
// server.js let express = require("express"); let app = express(); app.use(express.static(__dirname)); app.listen(3000);
// nginx.conf location / {// 代表输入/时默认去打开root目录下的html文件夹 root html; index index.html index.htm; } location ~.*.json{//代表输入任意.json后去打开json文件夹 root json; add_header "Access-Control-Allow-Origin" "*"; }http-proxy-middleware
NodeJS 中间件 http-proxy-middleware 实现跨域代理,原理大致与 nginx 相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置 cookieDomainRewrite 参数修改响应头中 cookie 中的域名,实现当前域的 cookie 写入,方便接口登录认证。
vue框架:利用 node + webpack + webpack-dev-server 代理接口跨域。在开发环境下,由于 Vue 渲染服务和接口代理服务都是 webpack-dev-server,所以页面与代理接口之间不再跨域,无须设置 Headers 跨域信息了。
module.exports = { entry: {}, module: {}, ... devServer: { historyApiFallback: true, proxy: [{ context: "/login", target: "http://www.proxy2.com:8080", // 代理跨域目标接口 changeOrigin: true, secure: false, // 当代理某些 https 服务报错时用 cookieDomainRewrite: "www.domain1.com" // 可以为 false,表示不修改 }], noInfo: true } }
非vue框架的跨域(2 次跨域)
nginx跨域
// 中间代理服务器 var express = require("express"); var proxy = require("http-proxy-middleware"); var app = express(); app.use( "/", proxy({ // 代理跨域目标接口 target: "http://www.proxy2.com:8080", changeOrigin: true, // 修改响应头信息,实现跨域并允许带 cookie onProxyRes: function(proxyRes, req, res) { res.header("Access-Control-Allow-Origin", "http://www.proxy1.com"); res.header("Access-Control-Allow-Credentials", "true"); }, // 修改响应信息中的 cookie 域名 cookieDomainRewrite: "www.proxy1.com" // 可以为 false,表示不修改 }) ); app.listen(3000);
// 服务器 var http = require("http"); var server = http.createServer(); var qs = require("querystring"); server.on("request", function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前台写 cookie res.writeHead(200, { "Set-Cookie": "l=a123456;Path=/;Domain=www.proxy2.com;HttpOnly" // HttpOnly:脚本无法读取 }); res.write(JSON.stringify(params)); res.end(); }); server.listen("8080");
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/96411.html
摘要:常用跨域方法总结为什么要跨域因为浏览器的一种安全机制同源策略的限制,导致不能直接获取不同源的资源,所以要跨域。那么什么才叫同源呢协议相同域名相同端口号相同图来自参见最后下面介绍常用的几种跨域方法。 常用跨域方法总结 为什么要跨域? 因为浏览器的一种安全机制——同源策略的限制,导致不能直接获取不同源的资源,所以要跨域。 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行...
摘要:关于项目的基本描述,参见开发微信商城项目总结之一项目介绍开发微信商城项目总结之二配置开发微信商城项目总结之三根据不同的开发环境做配置之前处理跨域问题是通过,但是只有开发环境是跨域的,代码打包后上传到服务器便不再跨域,所以在本地做了判断,判断 关于项目的基本描述,参见 vue开发微信商城项目总结之一–项目介绍 vue开发微信商城项目总结之二–Eslint配置 vue开发微信商城项目总结之...
摘要:常用跨域方法总结上篇文章介绍了几种常用的跨域方法常用跨域方法总结,本片为上一篇的补充,对比较重要的详细介绍。出于安全原因,从脚本内发起的跨源请求会受到一定限制。当开发者使用对象发起跨域请求时,它们已经被设置就绪。 常用跨域方法总结(2)——CORS 上篇文章介绍了几种常用的跨域方法:常用跨域方法总结,本片为上一篇的补充,对比较重要的Cross Origin Resource Shari...
阅读 631·2021-08-17 10:15
阅读 1715·2021-07-30 14:57
阅读 1970·2019-08-30 15:55
阅读 2813·2019-08-30 15:55
阅读 2703·2019-08-30 15:44
阅读 662·2019-08-30 14:13
阅读 2379·2019-08-30 13:55
阅读 2586·2019-08-26 13:56