跨域

为何跨域

由于浏览器需要不同站点之间通信,简单说如从http://www.baidu.com/ 页面去请求 http://www.google.com 的资源。此时就会存在跨域问题,好比只要锄头挥得好,哪有墙脚挖不倒,只要知识学得好,哪有工作不好找,只要工作找得好,哪有妹纸撩不倒,扯远了,大家懂的。。。

跨域是个啥

跨域是指浏览器从一个域名的网页去请求另一个域名的资源,此时浏览器同源策略会限制请求。而同源策略限制即只要协议,域名,端口有任何一个的不同,就被当作是跨域。即简单理解a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。那为什么有这个限制,当时设计浏览器那帮老头就想到女朋友不能共享,一切的一切出于安全考虑,否则就要刀光剑影乱套。

特别注意两点:第一,如果是协议和端口造成的跨域问题“前端”是无能为力的,第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。

如何解决跨域

1、 JSONP

/**

*JSONP(json+padding)(第三方框架如:jQuery)

*原理:采用全局方法或全局变量,会被自动加到window对象上(客户端定义回调函数,服务端响应调函数即fn(data)

*利用script标签中src天生跨域特性,除此

*还有a-href、img-src、iframe-src、*location.href等

*局限:只支持"GET请求",且不能解决不同域的页面间如何进行javascript调用问

*题,但兼容性好,且不受"同源策略的限制"。

*@param url 交互页面url即跨域地址

*@param type 跨域方式 即jsonp(方法)或var(变量) 默认jsonp

*@param success 跨域成功回调 处理传来的数据

*@param error 跨域失败回调

*/

function jsonp(url,success,error,type){

//回调函数名最好唯一即,以防止响应方调用时,调用到同名

​var prefix = "jsonp"+new Date().getTime();//前缀

​var defaultType = "jsonp",

​defaultCallback = prefix + "Callback";​​

​var jsonpType = /jsonp|var/.test(type)? type : defaultType;

​//创建默认回调函数

​if(defaultType == jsonpType){

​​/**在响应方调用,实参存在arguments数组中,默认值undefined

​​*  若指定形参接受时,就只能arguments[i]来获取,不用担心报越界。

​​*​

​​* data数据类型任意,可以是json,也可以是方法,还可以是基本类型

​​*/

​​window[defaultCallback] = function(data){

​​​if(success) success(data); //arguments[0]

​​};

}

//创建一个script的DOM对象

​var script = document.createElement("script");

​script.type = "text/javascript";

​//设置其同步属性

​script.async = true;

​//设置其地址

/**js中正则表达式另一种表示形式:/pattern/attributes 直接量,但是不能含    有变量

*注意:(1)不能加""或'',否则就变成字符串 (2)等价于 new RegExp(pattern","attributes")

​*pattern:即regexp

*attributes:其取值分别为i(大小写不敏感)、g(全局匹配,而非匹配第一个就停止)、m(多行匹配)

​*/

​/*script.src = url.replace(/#.*$/,"") +

​​ (/\?/.test(url) ? "&" : "?")+

​​type + "=" + defaultCallback;

​*/​

​script.src = url;//url绑定参数问题:url长度有限,其次需要解析出需要的参数(一般是?后面)​

​//设置jsonpType属性

/*

​if(!script.setAttribute){​

script.setAttribute(jsonpType,defaultCallback);//通过设置属性方式,ie8以前浏览器不支持

​}

​else*/

​​script.jsonpType = defaultCallback; //自定义属性

//设置监听,当浏览器把响应方加载完毕就及时“销毁”(安全考虑),但这样响应方就不需要再添加onload

​script.onload = script.onreadystatechange = function(){

​​//if(! this.readState === 'loaded' || this.readState === 'complete'){

​​if(! this.readState || /loaded|complete/.test(this.readState)){

​​​this.onload = this.onreadystatechange = null;

​​​if(jsonpType == "var"){

if(success) success(window[defaultCallback]); //响应方创建全局变量window[defaultCallback]

​​​}

​​​//移除该script的DOM对象,及其属性都会被移除

​​​if(this.parentNode) this.parentNode.removeChild(this);

​​​//删除全局函数或变量

​​​window[defaultCallback] = null;

​​}

​};​​

​addEvent(script,"error",error);

​//插入script到head中

​document.head.appendChild(script);​​

}

/**

*JSONP 漏洞

*由于浏览器解析HTML是按顺序解析即从上到下解析,

* 因此若要js操作HTML标签,则必须采用“浏览器加载完毕,才进行解析”。

* 即onload或onreadystatechange中readState判断。

*

*当使用JSONP时需要格外小心。

*js提供了两种访问成员的方式:

*​(1) 对象.成员

*​(2) 对象["成员"]

*js提供了两种声明变量的方式:

*​(1)var 变量 = 初始值;//默认的初始值undefined

*​(2)变量 = 初始值; //无默认值,因此不指定值而直接作为条件时,会报错

*/

(function(w){

​var getCallback = function(){//获取回调函数名

​var js = (window.document || document).head.getElementsByTagName("script");

​var callback = '';

​for(var i = 0 ; i < js.length ; i++){

​​/*

​​if(js[i].hasAttribute("jsonp")) return js[i].getAttribute("jsonp");

else if(js[i].hasAttribute("var"))return js[i].getAttribute("var");

else  ///\?.*(jsonp|var)/.test(src) ? src.split(/\?.*(jsonp|var)/) : "";

​​​//alert ( js[i].jsonpType );

​​*/

​​//js[i].jsonpType 不存在时,不会报错(由于此时对象成员默认undefined)且callback = undefined​

​​return (callback = js[i].jsonpType) ? callback : callback;

​}

​}

​//执行回调

​var response = function(callback,data){

​​var callback = getCallback();

​​if(typeof w[callback] === "function") w[callback](data); //type:jsonp 方法

else w[callback] = data; //type:var 变量

​}

​//response({name:"jsonp"}) 服务端

})(window);

jsonp("./proxy.js?aa",function(data){

//for(v in data)

​alert(data)

​},function(){

​​alert("失败")

​});

function test(){​

​var callback = "jsonpCallback";

​window[callback] = function(){}

alert(window[callback])

}

2、 DDI

/**

*DDI(document.domain+iframe) 父子域即主域相同,而二级域名不同。安全性低

*原理:只需要请求方与响应方的document.domain一致

*@param domain 域名

*@param url 交互页面url

*@method fn 回调函数 处理传来的数据

*/

function ddi(domain,url,fn){

document.domain = domain;

var ifr = document.createElement("iframe");//中间层即代理

ifr.src = url;

ifr.style.display = "none";

document.body.appendChild(ifr);

ifr.onload = ifr.onreadystatechange = function(){

var doc = ifr.contentDocument || ifr.contentWindow.document;

alert(doc.getElementsById("div"));

}

}

3、 LHI

/**

*LHI(location.hash+iframe) 父子域即主域相同,而二级域名不同。

*原理:只需要请求方与响应方的document.domain一致.

*注:若iframe下窗口(子窗口)想通过hash传递数据给其父窗口,

*即子窗口可以通过修改parent.location.hash值,

*但是ie、chrome的安全机制无法修改,因此需要在父窗口对应的域下创建一个空页面,

*并在该空页面添加parent.parent.location.hash = self.location.hash.substring(1);

*问题所在:

*​(1).信息暴露(url) (2).数据容量/类型有限

*​@param domain 域名

*​@param url 交互页面url

*​@method fn 回调函数 处理传来的数据

*/

function lhi(url,key,fn){

var ifr = document.createElement("iframe");//中间层即代理​​

​ifr.src = url+'#'+key;​​

​ifr.style.display = "none";

​document.body.appendChild(ifr);​​​

}

//lhi("./dialog.html","h");

function checkHash(){

try{

var data = location.hash ? location.hash.substring(1) : '';

if(!data) alert(data);

}catch(e){

​if(t) clearInterval(t);

​}

}

//var t = setInterval(checkHash,2000);

4、 WNI

/**

*WNI(window.name+iframe)

*安全性高、数据容量因浏览器而言(一般2M~32M)、数据类型任意

*原理:通过iframe的src属性将外域转向本域,

*​而跨域数据由iframe的window.name从外域传到本域。从而绕过浏览器的访问限制。

*​但需要代理页面即和本域同域名下的页面文件

*@param domain 域名

*@param url 交互页面url

*@method fn 回调函数 处理传来的数据

*/

function wni(url){

var state = 0;

//动态创建iframe

var ifr = document.createElement("iframe");//中间层即代理

ifr.style.display = "none";

/**信息交互页面即跨域页面,会发出请求,当不同域时,响应不回来。

​*​因此需要创建同域代理即ifr.contentWindow.location

​*/

​ifr.src = url ;

document.body.appendChild(ifr);

//加载外域传来数据

var loadfn = function(){

​if(state === 0){

​​state = 1;

ifr.contentWindow.location = "./proxy.html";//代理页面url即本域页面

}else if(state === 1){

​​//读取数据

​​var data = ifr.contentWindow.name;​​​

​​//操作

​​​alert("跨域数据:"+data)

​​​//销毁iframe,以保安全

​​​ifr.contentWindow.document.write('');

​​​ifr.contentWindow.close();

​​​document.body.removeChild(ifr);

​​}​​​​

};

​//事件绑定

​addEvent(ifr,"load",loadfn);​​​

}

5、 HTML5

/**

*HTML5 postMessage

*通过事件获取即event.data

*@param url 交互页面url

*@param data 交互数据

*@method 回调函数

*/

function cd(url,data,fn){

//动态创建iframe

var ifr = document.createElement("iframe");//中间层即代理

var targetOrigin = "*"; //用于限制调用postMessage方法的window,若*表示不限制

ifr.style.display = "none";

​ifr.src = url ;

document.body.appendChild(ifr);

ifr.contentWindow.postMessage(data,targetOrigin);

}

6、 CROS

/**

*CROS(Cross-Origin Resource Sharing) 跨域资源共享

*原理:使用自定义的HTTP头部让浏览器与服务器进行交互,从而决定请求或响应是应该失败还是成功。

CROS header简单的描述

Access-Control-Allow-Origin

多个域名可以用逗号隔开。如www.ios.com,www.android.com。*表示谁都可以,不限制域名(不建议使用*)。

Access-Control-Expose-Headers

设置浏览器允许访问的服务器的头信息的白名单,带Authorization令牌

Access-Control-Max-Age

在CROS协议中,一个AJAX请求被分成了两步。第一步OPTION为预检测请求,第二步为正式请求。请求的结果的有效期是多久,单位秒。

Access-Control-Allow-Credentials

是否允许请求带有验证信息,若要获取客户端域下的cookie时,需要将其设置为true。

Access-Control-Allow-Methods

资源可以被哪些方式请求GET, POST,TRACE等,多个值时用逗号分开,*为不受限制。

Access-Control-Allow-Headers

允许自定义的头部,逗号隔开。如:Origin , Accept ,Content-Type, x-requested-with, Authorization等。

*若请求两次:跨域请求时浏览器会自动发起一个 OPTIONS 到服务器,是一种预检测请求,用来检测是否安全。

*局限:兼容性不好即对以前浏览器如ie9以下且服务器会对其限制即需要服务器方法允许(参考后面spring)

*/

function xhr(url,type){

var xhr = new XMLHttpRequest();

xhr.onload = xhr.onreadystatechange = function(){

​fn(data);

}

xhr.open(type,url,true);

xhr.send();

}

或jQuery中ajax的headers/beforeSend配置或crossDomain:true 若要发送cookie配置则xhrFields:{ withCredentials:true }

7、 其他

/**

*服务代理

*如:nginx代理

*/

继cros

1.采用Spring @CrossOrigin注解

@CrossOrigin(origins = "url", maxAge = 10)

​2.过滤器方式

public class CORSFilter implements Filter {

  @Override

  public void destroy() {

      System.out.println("Filter-destroy");

  }

  @Override

  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)

          throws IOException, ServletException {

      //String origin = (String) servletRequest.getRemoteHost()+":"+servletRequest.getRemotePort();

      HttpServletRequest request = (HttpServletRequest) servletRequest;

      System.out.println("Filter-Method:" + request.getMethod()); // GET, POST, OPTIONS

      System.out.println("Filter-Authorization:" + request.getHeader("Authorization"));

      HttpServletResponse response = (HttpServletResponse) servletResponse;

      response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");

      response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");

      response.setHeader("Access-Control-Max-Age", "10");

      response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");

      response.setHeader("Access-Control-Allow-Credentials", "true");

      filterChain.doFilter(servletRequest, servletResponse);

  }

  @Override

  public void init(FilterConfig arg0) throws ServletException {

      System.out.println("Filter-init");

  }

}

或继承springMVC中OncePerRequestFilter类

public class CorsFilter extends OncePerRequestFilter {

  @Override

  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

      if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {

          // CORS "pre-flight" request

          response.addHeader("Access-Control-Allow-Origin", "*");

          response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");

          response.setHeader("Access-Control-Allow-Headers", "x-requested-with");

          response.addHeader("Access-Control-Max-Age", "1800");//30 min

      }

      //This will filter your requests and responses.

      filterChain.doFilter(request, response);

  }

}

或springboot

package cn.ucmed.otaku;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;

import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ImportResource;

import org.springframework.web.cors.CorsConfiguration;

import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import org.springframework.web.filter.CorsFilter;

@EnableZuulProxy

@SpringBootApplication(exclude = {

      DataSourceAutoConfiguration.class,      HibernateJpaAutoConfiguration.class})

@ImportResource("classpath*:META-INF/spring/dubbo.xml")

public class GatewayApplication {

  public static void main(String[] args) {      SpringApplication.run(GatewayApplication.class, args);

  }

  @Bean

  public CorsFilter corsFilter() {

      final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

      final CorsConfiguration config = new CorsConfiguration();

      config.setAllowCredentials(true);

      config.addAllowedOrigin("*");

      config.addAllowedHeader("*");      config.addAllowedMethod("OPTIONS");

      config.addAllowedMethod("HEAD");

      config.addAllowedMethod("GET");

      config.addAllowedMethod("PUT");

      config.addAllowedMethod("POST");      config.addAllowedMethod("DELETE");

      config.addAllowedMethod("PATCH");

      config.addExposedHeader("token");

  source.registerCorsConfiguration("/**", config);

      return new CorsFilter(source);

  }

}

/**

*flash技术

*/​​​

/**

*事件绑定

*@param tag 标签

*@param type 事件类型,注没有"on"

*@param handle 事件句柄函数即"函数名"

*/

function addEvent(tag,type,handle){

//try...catch 避免判断浏览器

try{

//false,冒泡(默认);true,捕获。指定事件是否在捕获或冒泡阶段执行。

​//Chrome、FireFox、Opera、Safari、IE9及其以上

​tag.addEventListener(type,handle,false);//可以绑定多个事件

}catch(ex){

​try{

​​//IE9及其以下

​​​tag.attachEvent('on' + type,handle);

​​}catch(ex){

​​​//

​​​tag['on' + type] = handle;

​​}

}

}

function proxy(url,obj,fn){//实参一般为匿名方法,也可以为有方法名

//方法作为形参时,不能如fn(参数)。即方法名可以作形参

//而方法作实参如fn(参数),那么在此直接就执行,而不会在调用fn 即方法调用:方法名(实参)

var ifr = document.createElement("iframe");//中间层即代理

ifr.src = url+(obj? obj : '');

ifr.style.display = "none";

try{//防止未给定方法而异常

fn();//由于函数调用如function(){}()或(function 方法名(){})(参数)等形式

​alert(fn)

}catch(e){}

document.body.appendChild(ifr);

}

/*

var hash_url = window.location.hash;

var datas = hash_url.split('#')[1].split('&');

function dialog(){

var iframe = document.createElement("iframe");

//iframe.style.display = 'none';

var state = 0;

iframe.src = "./dialog.html";

document.body.appendChild(iframe);

iframe.onload = function(){

if(state === 1){

//alert(iframe.contentWindow.name);

//iframe.contentWindow.close();

//document.body.removeChild(iframe);

}else if(state === 0){

state = 1;

​//iframe.contentWindow.location = './proxy.html';

​iframe.src = "./proxy.html";

}​​​​

}*/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345