域名系统DNS以及跨域问题
域名到Ip地址解析是由分布在因特网上的许多域名服务器程序共同完成的。运行域名服务器程序的机器是域名服务器
域名到ip地址的解析过程:
当一个应用进程需要把主机名解析为ip地址时,该应用就调用解析程序,并成为dns的一个客户,把待解析的域名放在dns请求报文中,以udp用户数据报方式发送给本地域名服务器(使用udp是为了减少开销)。本地域名服务器在查找域名后,把对应的ip地址放在回答报文中返回,应用进程获得目的主机的IP地址后即可进行通信。
若本地域名服务器不能回答该请求,则此域名服务器暂时成为dns中的另一个客户,并向其他域名服务器发出查询请求,这种过程直到找到能够回答该请求的域名服务器为止
域名结构:
因特网采用了层次树状结构的命名方法,例如:
mail.cctv.com (从左到右依次是三级域名.二级域名.顶级域名)
如果缺少同源策略,浏览器容易收到XSS,CSFR的攻击,所谓同源是指“协议+域名+端口”
⚠️:
1.协议和端口造成的跨域问题是“前台”无法解决的
2.在跨域问题上,仅仅通过url的首部来识别,而不会根据域名对应的IP地址是否相同来判断。URL的首部可以理解为“协议,域名和端口必须匹配
3.跨域不是请求发不出去,是请求能发出去,服务器端能收到请求并且正常返回结果,只是结果被浏览器拦截了
解决方案:
1.jsonp
1.jsonp原理
利用<script>标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的json数据,jsonp请求一定需要对方服务器做支持才可以
2.jsonp和ajax对比:
jsonp和ajax相同,都是客户端想服务器端发送请求,从服务器端获取数据的方式,但是ajax属于同源请求,而jsonp是非同源请求
3.jsonp仅限与get方法,并且不安全,容易收到xss攻击
4.实现流程:
- 声明一个回调函数,其函数名当做参数值,要传递给跨域请求数据的服务器,函数形参主要为获取目标数据
- 创建一个<script>标签,把哪个跨域的api数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名
- 服务器收到请求后,需要进行特殊处理:把传递进来的函数名和它需要给你的数据拼成泥想要的数据
- 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数,对返回的数据进行操作
用jq实现:(jsonp是get和异步请求,不存在其他的请求方式和同步请求,且JQ默认就会jsonp的请求清除缓存)
$('#search_input').on('keyup',function(event){ var searText=$('#search_input').val(); $.ajax({ type:'get', async:true, dataType:'jsonp', jsonp:'cb', url:'http://api.bing.com/qsonhs.aspx?type=cb&q='+searchText, success:function(data){ var data=data.AS.Results[0].Suggests; var html=''; for(var i=0;i<data.length;i++){ html+='<li>'+data[i].Txt+'</li>'; } $('#search-result').html(html); $('#search-suggest').show().css({ top:$('#search-form').offset().top+$('#search-form').height()+10; }) } }) })
2.cors
cors需要浏览器和后端同时支持,ie8和ie9需要通过XDomainRequest来实现
浏览器会自动进行cors通信,实现cors通信是后端实现
通过这种方式解决跨域问题,会在发送请求时出现两种情况,分别为简单请求和复杂请求
1)简单请求:
满足以下条件,属于简单请求:
条件1:使用下列方法之一:
- get
- head
- post
条件2:content-type的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
⚠️:
请求中的任意XMLHttpRequestUpload对象均没有注册任何事件监听器;XMLHttpRequestUpload对象可以使用XMLHttpRequest.upload属性访问
2.复杂请求:
不符合以上条件的请求就是复杂请求
复杂请求的cors请求,会在正式通信之前,增加一次HTTP查询请求,称为“预检”请求,该请求是option方法的,通过该请求来知道服务器是否允许有跨域请求
我们用put向后台请求时,属于复杂请求,后台需要如下配置:
res.setHeader('Access-Control-Allow-Methods','PUT')//access-control-allow-methods:预检请求的应答中明确了客户端所要访问的资源允许使用的方法或方法列表 res.setHeader('Access-Control-Max-Age',6)//响应的首部表示预检请求的返回结果可以被缓存多久 //响应首部Access-Control-Allow-Headers:预检请求,列出了将会在正式请求中access-control-headers字段中出现的首部信息 if(req.method==='OPTIONS'){ req.end() } app.put('/getData',function(req,res){ console.log(req.headers); res.end('hello'); })
一个完整的复杂请求的例子,并介绍cors请求的相关字段
let xhr=new XMLHttpRequest() document.cookie='name=hello'//cookie不能跨域 xhr.withCredentials=true; //一个boolean类型,指示了是否该使用类似cookies,authorization headers或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制请求。在同一个站点下使用withCredentials属性是无效的 xhr.open('PUT','http://localhost:4000/heoo',true) xhr.setRequestHeader('name','yuanq') xhr.onreadystatechange=function(){ if(xhr.readyState===4){ if((xhr.status>=200&&xhr.status<300)||xhr.status===304) { console.log(xhr.response); console.log(xhr.getResponseHeader('name')); } } } xhr.send() //server1.js let express=require('express'); let app=express(); app.use(express.static(_dirname)); app.listen(3000) let express=require('express'); let app=express(); let whitList=['http://localhost:3000'] //设置白名单 app.use(function(req,res,next){ let origin=req.headers.origin if(whiteList.includes(origin)){ //设置可以访问的源 res.setHeader('Access-Control-Origin',origin) //允许携带哪个头访问我 res.setHeader('Access-Control-Headers','name') //允许哪个方法可以访问我 res.setHeader('Access-Control-Allow-Methods','PUT') //允许哪个携带cookie res.setHeader('Access-Control-Allow-Credentials',true) //预检的存活时间 res.setHeader('Access-Control-Max-Age',6) //允许返回头 res.setHeader('Access-Control-Expose-Headers','name') if(req.method==='OPTIONS'){ res.end() } } next() }) app.put('/getData',function(req,res){ console.log(req.headers); res.setHeader('name','jeo') res.end('hello') }) app.get('/getData',function(req,res){ console.log(req.headers); res.end('hello') }) app.use(express.static(_dirname)) app.listen(5000)
3.websocket(支持ie10以上的版本)
websocket是h5的持久化协议,它实现了浏览器与服务器的全双工通信,同时也是解决跨域的一种方案。websocket是一种双向通信协议,在建立连接之后,websocket的server与client都能主动向对方发送或者接受数据,websocket在建立连接时需要借助http协议,连接好之后client与server之间的双向通信与http无关了
原生的websocket api使用不方便,使用socket.io,它可以封装websocket接口,也对不支持websocket的浏览器提供了向下兼容
首先:本地文件index.html向localhost:233发送数据和接受数据
<script> let socket=new WebSocket('ws://localhost:233'); socket.onopen=function(){ socket.send('hl') } socket.onmessage=function(e){ console.log(e.data); } </script> //服务器端 let express=require('express'); let app=express(); let WebSocket= require('nodejs-websocket') let wss=new WebSocket.Server({port:3000}) wss.on('connection',function(ws){ ws.on('message',function(data){ console.log(data); ws.send('hell') }) })