转载来源:https://www.cnblogs.com/ypSharing/p/corsHanlder.html
目录
不受同源策略限制的
<script src="..."></script>,<img>,<link>,<iframe>
等。受到限制的
<script>
标签进行解析执行通过script标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
<script>
function getData(data){
console.log(data)
}
</script>
<script src="http://127.0.0.1:3000/web?cb=getData"></script>
后端nodejs代码 主要用来模拟服务器 携带参数必须是字符串
const express=require('express')
const router=express.Router()
router.get('/web',(req,res)=>{
let {cb}=req.query
console.log(req.query)
var data = {
name: 'xtt',
age: 18,
gender:'女孩子'
}
// 携带参数必须是字符串
res.send(`${cb}(${JSON.stringify(data)})`)
router.get('/que',(req,res)=>{
res.send(`${req.query.cb}('dd')`)
})
})
module.exports=router
以jquery来发起jsonp请求
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.10.0/jquery.js"></script>
<script>
let url = 'http://127.0.0.1:3000/que?cb=getData'
$.ajax({
method: 'GET',
url,
dataType: 'jsonp',
success: (res) => {
console.log(res)
}
})
</script>
handleCallback({"success": true, "user": "admin"})
this.$http = axios;
this.$http.jsonp('http://127.0.0.1:3000/que?cb=getData', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
浏览器在发送跨域请求的时候,会先判断下是简单请求还是非简单请求,如果是简单请求,就先执行服务端程序,然后浏览器才会判断是否跨域。 同时满足以下的两个条件,就属于简单请求。浏览器对这两种的处理,是不一样的。
详细描述 对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。 举例:
对服务器提出特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
预检请求
举例
服务器收到“预检”请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应
HTTP回应中,除了关键的是Access-Control-Allow-Origin字段,其他CORS相关字段如下:
1)前端设置
let xhr;
try {
xhr=new XMLHttpRequest();
} catch (error) {
xhr=new ActiveXObject('Microsoft.XMLHTTP');
}
xhr.open('post','http://localhost:3000/login',true);
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
xhr.send('name=111&age=12');
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
let reg=/^2\d{2}/
if(reg.test(xhr.status)){
console.log(JSON.parse(xhr.response))
}
}
}
nodejs代码 在Express中通过第3方中间件来完成cors跨域解决 使用步骤分为如下 3 步:
const express=require('express')
const cors=require('cors')
const app=express()
app.listen(3000)
const allowHosts=[
'http://localhost:5000',
'http://localhost:2000'
]
app.use(cors())
app.use((req,res,next)=>{
let hst =req.header.origin
if(allowHosts.includes(hst)){
next()
}else{
return res.send({
code:404,
msg:'地址不对'
})
}
})
app.get('/login',(req,res)=>{
res.send('登陆')
})
提到代理,肯定要说一下这两个的区别。
浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。
location / {
add_header Access-Control-Allow-Origin *;
}
跨域问题:同源策略仅是针对浏览器的安全策略。服务器端调用HTTP接口只是使用HTTP协议,不需要同源策略,也就不存在跨域问题。
实现思路:通过Nginx配置一个代理服务器域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域访问。
nginx具体配置
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.yp2.com:8080; #反向代理
proxy_cookie_domain www.yp2.com www.yp1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.yp1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
使用node + express + http-proxy-middleware搭建一个proxy服务器。
npm i express htttp-proxy-middleware
const express=require('express')
const app=express()
app.listen(5000)
const httpProxyMiddleware=require('http-proxy-middleware')
// 服务器代理 ---接口中间层 代理层
app.use('/api' ,httpProxyMiddleware.createProxyMiddleware({
// 代理的地址
target:'http://localhost:8989',
// 默认false不修改。修改代理请求是他的主机名
changeOrigin:true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://localhost:5000');
res.header('Access-Control-Allow-Credentials', 'true');
},
// 匹配规则
pathRewrite:{
// 访问路径 映射到 目标服务器中的路径
'^/v1/api':'/'
}
}))
vue中实现开发环境的时的反向代理进行跨域解决,在项目根目录下面创建一个vue.config.js文件,写下如下代码 vue.config.js部分配置:
module.exports={
// 指定服务器模块
devServer:{
// 代理
proxy:{
'/v1/api':{
// 目标地址
target:'http://localhost:3000',
changeOrigin:true,
pathRewrite:{
'/v1/api':'/api'
}
}
}
}
}
这两个域名必须属于同一个一级域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域。 Javascript出于对安全性的考虑,而禁止两个或者多个不同域的页面进行互相操作。 而相同域的页面在相互操作的时候不会有任何问题。
alert(document.domain = "baidu.com"); //"baidu.com"
alert(document.domain = "www.baidu.com"); //"www.baidu.com"
1)父窗口:(http://father.baidu.com/a.html)
<iframe id="iframe" src="http://child.baidu.com/b.html"></iframe>
<script>
document.domain = 'baidu.com';
var user = 'admin';
</script>
预览 1)子窗口:(http://child.baidu.com/b.html)
<script>
document.domain = 'baidu.com';
// 获取父窗口中变量
console.log('get js data from parent ---> ' + window.parent.user);
</script>
hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。
1)a.html:(http://www.baidu1.com/a.html)
<iframe id="iframe" src="http://www.baidu2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
// 向b.html传hash值
setTimeout(function() {
iframe.src = iframe.src + '#user=admin';
}, 1000);
// 开放给同域c.html的回调方法
function onCallback(res) {
alert('data from c.html ---> ' + res);
}
</script>
2)b.html:(http://www.baidu2.com/b.html)
<iframe id="iframe" src="http://www.baidu1.com/c.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
// 监听a.html传来的hash值,再传给c.html
window.onhashchange = function () {
iframe.src = iframe.src + location.hash;
};
</script>
3)c.html:(http://www.baidu1.com/c.html)
<script>
// 监听b.html传来的hash值
window.onhashchange = function () {
// 再通过操作同域a.html的js回调,将结果传回
window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
};
</script>
window.name属性的独特之处:只要在一个window下,无论url怎么变化,只要设置好了window.name,那么后续就一直都不会改变。同理,在iframe中,即使url在变化,iframe中的window.name也是一个固定的值,利用这个,我们就可以实现跨域了(2MB)。
test1.html
<body>
<h2>test1页面</h2>
<iframe src="http://192.168.0.1/php_demo/test2.html" frameborder="1"></iframe>
<script>
var ifr = document.querySelector('iframe')
ifr.style.display = 'none'
var flag = 0;
ifr.onload = function () {
console.log('跨域获取数据', ifr.contentWindow.name);
ifr.contentWindow.close();
}
</script>
</body>
test2.html
<body>
<h2>test2页面</h2>
<script>
var person = {
name: '大鹏_yp',
age: 24,
school: 'lngydx'
}
window.name = JSON.stringify(person)
</script>
</body>
通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
<iframe id="iframe" src="http://www.baidu2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向domain2传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.baidu2.com');
};
// 接受baidu2返回数据
window.addEventListener('message', function(e) {
alert('data from baidu2 ---> ' + e.data);
}, false);
</script>
2)b.html:(http://www.baidu2.com/b.html)
<script>
// 接收baidu1的数据
window.addEventListener('message', function(e) {
alert('data from baidu1 ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// 处理后再发回baidu1
window.parent.postMessage(JSON.stringify(data), 'http://www.baidu1.com');
}
}, false);
</script>
WebSocket 如何工作 Web浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。由于 WebSockets 连接长期存在,与典型的HTTP连接不同,对服务器有重要的影响。
基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器。
1)前端代码:
<div>user input:<input type="text"></div>
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
<script>
var socket = io('http://www.baidu2.com:8080');
// 连接成功处理
socket.on('connect', function() {
// 监听服务端消息
socket.on('message', function(msg) {
console.log('data from server: ---> ' + msg);
});
// 监听服务端关闭
socket.on('disconnect', function() {
console.log('Server socket has closed.');
});
});
document.getElementsByTagName('input')[0].onblur = function() {
socket.send(this.value);
};
</script>
2)Nodejs socket后台:
var http = require('http');
var socket = require('socket.io');
// 启http服务
var server = http.createServer(function(req, res) {
res.writeHead(200, {
'Content-type': 'text/html'
});
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
// 监听socket连接
socket.listen(server).on('connection', function(client) {
// 接收信息
client.on('message', function(msg) {
client.send('hello:' + msg);
console.log('data from client: ---> ' + msg);
});
// 断开处理
client.on('disconnect', function() {
console.log('Client socket has closed.');
});
});
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。