当前位置:网站首页>关于跨域问题
关于跨域问题
2022-08-02 03:06:00 【M78_国产007】
跨域
浏览器为了保护用户隐私安全,采用同源策略,当用户在网络中做AJAX网络请求时,浏览器会判断当前页面和请求资源的网址是不是同一个服务器,如果不是浏览器就会拒绝接收服务器发送的数据(但是我们要知道服务器是给我们返回了数据的,只是浏览器拒绝接收数据)。
简单来说:跨域,简单理解为跨的两个不同的服务器,其中我们所熟知的AJAX技术受跨域影响,但是网页中标签的src并不会受到跨域影响。
那么同源策略是什么呢?
同源策略,即两个网址的 协议、ip、端口三种一样代表同源。三者有一者不同则代表不同源。
举两个小例子判断两个网址是否同源:
https://www.abc.com/index.html
https://www.abc.com/home/goods/ajax1
是同源 协议 https相同,这里的www.abc.com为域名(域名=ip+端口),这三者相同就同源了,不用再比较后面的查询字符。
https://www.abc.com/index.html
http://www.abc.com/second.html
不是同源,一个协议是http,一个是http。
https://www.abc.com/index.html
http://www.abc.cn/second.html
不是同源,www.abc.com和www.abc.cn用DNS解析出来的ip+端口不相等。
既然网页资源获取时,会存在跨域问题,那么我们用什么方法解决呢?
解决跨域的办法
1、CORS跨域资源共享
我们先测试一下,在页面中获取不同服务器中的数据。
浏览器报错:
我们翻译一下:
它说我们没有设置 'Access-Control-Allow-Origin'头,那我们设置一下。
在8080端口服务器中我们的接口数据,返回数据包前面设置头部信息:
res.setHeader("Access-Control-Allow-Origin","http://192.168.6.9:8081")
res.end('{"name":"zs","age":"55"}')
这里的http://192.168.6.9:8081也可以写成*号,代表所有网页都能访问。
再次获取数据:
即可成功获取。
总结一下:在数据包的头部配置Access-Control-Allow-Origin字段以后,数据包发送给浏览器后;浏览器就会根据这里配置的白名单 "放行" 允许白名单的服务器对应的网页来用AJAX跨域访问。
2、JOSNP
先了解一下JSONP的原理实现:
JSONP全称为JSON with Padding,是针对JSON数据的获取,是一种可以绕过同源策略的方法。
实现不再依靠AJAX请求跨域服务器资源,而是用script标签的src属性去跨域请求资源,不会报跨域错误,但是因为是用script的src属性请求过来的,所以它会直接用浏览器的v8引擎直接运行获取的编码;
因为获取过来的JSON数据需要一个值来接收,我们一般采用函数的传参的方式传递,形式如下:
' fn({"name":"karen"}) ',请求完毕后,v8引擎直接运行这个代码,代码fn()就会调用,因此我们要提前在全局作用域下声明这个函数,通过形参接收传过来的JSON数据。
函数如:
function fn(arg){
var arg1=JOSN.parse(arg)
console.log(arg1)//由此就可得到JOSN对象操作页面
}
这个方法现在有一个问题,就是后端发过来的函数名要跟我们在前端中设置的函数名相同才行,当前端函数名改变时,后端我们也需要手动修改,非常不方便,并且这样设置的函数名很有可能造成变量污染,因此需要优化。
优化:
前端可以通过querystring把函数名以参数的形式发送给后端,后端通过url模块取出quesystring字
段中的函数名。传递的函数名设置为含有时间戳的变量名,
如:var name1="name"+new Date().getTime()
原理讲完了,我们动手实践一下:
封装一个router模块,方便后端的编写,创建服务器时直接导入就可帮我们创建好服务器;帮我们实现静态托管,默认读取文件在public,我们也可以通过router.static()修改默认静态托管路径;我还可以通过router.get()方法注册接口。
var fs=require("fs")
var url=require("url")
var querysting=require("querystring")
var mime=require("mime")
let urls={}
var http=require("http")
let router=function(req,res){
//这个函数每次用户访问时运行
let pathname=url.parse(req.url).pathname
fs.readFile(router.basepath+pathname,(err,data)=>{
if(!err){
res.setHeader("content-Type",mime.getType(pathname))
res.end(data)
}else{
if(!urls[pathname]){res.end("404 not found-mymvc")}
else{urls[pathname](req,res)}
}
})
}
router.static=function(path){
this.basepath=path
}
router.get=function(url,cb){
urls[url]=cb
}
router.basepath=__dirname+"/public"
http.createServer(router).listen(8080)
module.exports=router;
编写8080端口主服务器(被获取数据)
const router=require("./router.js")
const querystring=require("querystring")
const url=require("url")
router.get("/data",function(req,res){
//获取querystring
var querys=url.parse(req.url).query
//转为对象
var querobj=querystring.parse(querys)
//获得函数名
var callback=querobj.callback
//假数据
let a=`{"name":"ls","age":11,"horry":"sing"}`
res.end(`${callback}(${a})`)
})
编写8081端口下托管页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>首页</h1>
<button onclick="fn()">点击获取主服务器的数据</button>
<script>
function fn(){
var name1="name"+new Date().getTime()
window[name1]=function(arg){
console.log(arg);
}
var script1 = document.createElement("script")
script1.src=`http://192.168.6.9:8080/data?callback=${name1}`
document.body.appendChild(script1)
}
</script>
</body>
</html>
记得导入mime模块,它是第三方模块,router.js模块内有导入它,所以两个文件都应npm i mime;
文件结构:
代码设计完毕,同时打开两个服务器,运行浏览器,获取data接口数据。
3、Proxy代理服务
proxy原理十分简单,同时它也是三种解决跨域问题中使用最多的。
它利用自己的前端页面去请求自己的后端AJAX接口,然后再利用后端去请求别的服务器的AJAX接口,再把请求到的数据返回给自己的前端页面。这样就不会存在跨域限制。
画出流程图:
用自己的后端去请求其他服务器的数据接口,需要引入一个第三方模块——request
用法:request(url,function(err,status,body)) 默认是get请求,可以指定具体用那种请求,如request.post(url,(err,status,body)=>{})post请求,在request后面点具体的请求方式,不写默认get请求。
参数说明:
err 请求失败的错误信息;
status 状态码;
body 请求得到的接口数据。
直接上手练一练:
前端页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="fn()">通过服务器代理获取数据</button>
<script>
function fn(){
//AJAX请求数据
let xhr=new XMLHttpRequest()||new ActiveXObject("Microsoft.XMLHTTP")
xhr.open("GET","http://172.20.10.3:8080/ajax1")
xhr.send()
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
console.log(xhr.responseText);
}
}
}
</script>
</body>
</html>
同一服务器的后端页面:
const router=require("./router.js")
const request=require("request")
router.get("/ajax1",function(req,res){
request("http://172.20.10.3:8081/ajax2",function(err,status,body){
res.end(body)
})
})
另外的服务器的接口数据:
const router=require("./router.js")
router.get("/ajax2",function(req,res){
res.end('{"name":"zs"}')
})
运行看一下效果:
边栏推荐
猜你喜欢
随机推荐
Redis主从、哨兵、 Cluster集群一锅端!
暴力破解全攻略
Duplicate entry ‘XXX‘ for key ‘XXX.PRIMARY‘解决方案。
运维理想和现实,你是?
MySQL修改最大连接数限制
请教各位大佬,如果我代码里面设置了,这个id我在什么地方可以查到呢?连接到mysql cluste
(1) Redis: Key-Value based storage system
MySQL中的存储过程(详细篇)
* Compare version numbers
【LeetCode】145. Postorder Traversal of Binary Tree
AcWing 1053. Repair DNA problem solution (state machine DP, AC automata)
直击程序员面试现场:百度面试官都问了我些啥?
aws s3 upload file
STM32——LCD—TFTLCD原理与配置介绍
PHP WebSehll backdoor script and detection tool
1. 获取数据-requests.get()
两对象数组比较拿出不同值方法
MySQL中根据日期进行范围查询
7-35 城市间紧急救援 (25 分)c语言(测试点二未通过)
【LeetCode】144.二叉树的前序遍历