跨域访问

 

通俗地讲,只有域名和端口号一致,算同域,其他的所有都是跨域. 按照定义,一个域是由schema、host、port三者共同组成,与路径无关。所谓跨域,是指在http://example-foo.com/域上通过XMLHttpRequest对象调用http://example-bar.com/域上的资源。

我所知道的跨域有如下几种方式

  1. 服务器代理
  2. jsonp(<script>标签)
  3. <Iframe>标签(没用过)
  4. CORS规范

第一种服务器代理,不说了. 第二种jsonp是利用<script>标签没有浏览器同源策略的限制而发展起来的 第三种.没用过,作为刚迈入这一行的猿,都是$.ajax()学习js的.前端h5的流行趋势是单页面应用.不停的ajax. 第四种是最近知道的.最近在百度的apistore找到一个免费的天气api.去练习学习这种规范,直接在前端发起请求,不需要后端代理,大大减轻工作量.

jsonp

HTML

<script type="text/javascript">
        function ggg(result){
            console.log(result);
            alert(result);
        }
</script>
<script type="text/javascript" 
src="http://localhost:8080/h5ai/action/select.do?jsoncallback=ggg"></script>

JAVA

@Controller
@RequestMapping("/action")
public class FileController {

    @Resource
    private FileService fileService;

    //jsonp方式
    @RequestMapping("/select.do")
	public String getFiles(String path,HttpServletRequest request,String jsoncallback){
		String json=new Gson().toJson(fileService.getFile(path,request));
		return jsoncallback+"("+json+")";  
	}

    //json
    @RequestMapping("/select1.do")
	@ResponseBody
	public Result getFile(String path,HttpServletRequest request){
		return fileService.getFile(path,request);
	}

}	

原理:浏览器发get请求,带的参数名为jsoncallback(可自定义),参数值为处理数据的function名.

服务器收到请求后,按照参数值拼接返回语句,上述示例中的function名为ggg,服务器拼接String语句ggg(data).data是浏览器需要的json数据.

浏览器加载html文件后,script标签去请求文件内容,请求到后,去执行function(qqq).去执行ggg() function.去完成jsonp方式的调用

复杂一点,script添加多个参数,服务器根据参数值,生成json值.简单的远程调用方法.

CORS

CORS把HTTP请求分成两类,不同类别按不同的策略进行跨域资源共享协商。

  1. 简单跨域请求。当HTTP请求出现以下两种情况时,浏览器认为是简单跨域请求: 1). 请求方法是GET、HEAD或者POST,并且当请求方法是POST时,Content-Type必须是application/x-www-form-urlencoded, multipart/form-data或着text/plain中的一个值。 2). 请求中没有自定义HTTP头部。 对于简单跨域请求,浏览器要做的就是在HTTP请求中添加Origin Header,将JavaScript脚本所在域填充进去,向其他域的服务器请求资源。服务器端收到一个简单跨域请求后,根据资源权限配置,在响应头中添加Access-Control-Allow-Origin Header。浏览器收到响应后,查看Access-Control-Allow-Origin Header,如果当前域已经得到授权,则将结果返回给JavaScript。否则浏览器忽略此次响应。
  2. 带预检(Preflighted)的跨域请求。当HTTP请求出现以下两种情况时,浏览器认为是带预检(Preflighted)的跨域请求: 1). 除GET、HEAD和POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他HTTP方法。 2). 请求中出现自定义HTTP头部。 带预检(Preflighted)的跨域请求需要浏览器在发送真实HTTP请求之前先发送一个OPTIONS的预检请求,检测服务器端是否支持真实请求进行跨域资源访问,真实请求的信息在OPTIONS请求中通过Access-Control-Request-Method Header和Access-Control-Request-Headers Header描述,此外与简单跨域请求一样,浏览器也会添加Origin Header。服务器端接到预检请求后,根据资源权限配置,在响应头中放入Access-Control-Allow-Origin Header、Access-Control-Allow-Methods和Access-Control-Allow-Headers Header,分别表示允许跨域资源请求的域、请求方法和请求头。此外,服务器端还可以加入Access-Control-Max-Age Header,允许浏览器在指定时间内,无需再发送预检请求进行协商,直接用本次协商结果即可。浏览器根据OPTIONS请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。这个过程对真实请求的调用者来说是透明的。 XMLHttpRequest支持通过withCredentials属性实现在跨域请求携带身份信息(Credential,例如Cookie或者HTTP认证信息)。浏览器将携带Cookie Header的请求发送到服务器端后,如果服务器没有响应Access-Control-Allow-Credentials Header,那么浏览器会忽略掉这次响应。 这里讨论的HTTP请求是指由Ajax XMLHttpRequest对象发起的,所有的CORS HTTP请求头都可由浏览器填充,无需在XMLHttpRequest对象中设置。以下是CORS协议规定的HTTP头,用来进行浏览器发起跨域资源请求时进行协商:
  3. Origin。HTTP请求头,任何涉及CORS的请求都必需携带。
  4. Access-Control-Request-Method。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的方法。
  5. Access-Control-Request-Headers。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的自定义Header列表。
  6. Access-Control-Allow-Origin。HTTP响应头,指定服务器端允许进行跨域资源访问的来源域。可以用通配符*表示允许任何域的JavaScript访问资源,但是在响应一个携带身份信息(Credential)的HTTP请求时,Access-Control-Allow-Origin必需指定具体的域,不能用通配符。
  7. Access-Control-Allow-Methods。HTTP响应头,指定服务器允许进行跨域资源访问的请求方法列表,一般用在响应预检请求上。
  8. Access-Control-Allow-Headers。HTTP响应头,指定服务器允许进行跨域资源访问的请求头列表,一般用在响应预检请求上。
  9. Access-Control-Max-Age。HTTP响应头,用在响应预检请求上,表示本次预检响应的有效时间。在此时间内,浏览器都可以根据此次协商结果决定是否有必要直接发送真实请求,而无需再次发送预检请求。
  10. Access-Control-Allow-Credentials。HTTP响应头,凡是浏览器请求中携带了身份信息,而响应头中没有返回Access-Control-Allow-Credentials: true的,浏览器都会忽略此次响应。

总结:只要是带自定义header的跨域请求,在发送真实请求前都会先发送OPTIONS请求,浏览器根据OPTIONS请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。所以复杂请求肯定会两次请求服务端。

这是一个查询北京天气的接口请求demo

前端

<script type="text/javascript">
	$.ajax({  
	   url: "http://apis.baidu.com/thinkpage/weather_api/suggestion",
	   data:{"location":"beijing","language":"zh-Hans","unit":"c","start":"0","days":"3"},
	   dataType: 'json',  
	   type: 'GET',  
	   beforeSend: function (xhr) {  
		   xhr.setRequestHeader("apikey", "■■■■■■■■■■■■■■■■■■■■■■■■");  
	   },  
	   async: false,  
	   cache: false,  
	   //contentType: 'application/x-www-form-urlencoded',  
	   success: function (sResponse) {
		   //console.log(sResponse);
		   var x=eval(sResponse);
		   console.log(x.results[0].location.id);
	   }  
   });  

</script>

后端 java可以直接在代码中,设置响应的消息头

//
//允许跨域访问  
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");  
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT");  
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Test");  

也可以在toncat中设置,主要是添加cors相应jar.web.xml中添加filter 具体配置请自行谷歌,本人没实践过.

总结:

假如你要代表你们公司去某公司拿资料.前台把你拦住,不让进,你去找自己公司的领导,领导帮你把资料拿回来了,告诉你结果.这就是典型的服务器代理,每次都得让领导帮忙.

时间长了,领导不让了,让你自己想办法,你花了大笔价钱,让对方公司同意和你进行一项”秘密”活动,让你得到对方的数据.其他同行一打听,原来你们是这么干的啊,他们也这么干了.-jsonp

最后对方说,这么干,不行,我们的资料不安全,外面的谁都能拿到..于是你们又定了一个约定.每次你去找前台,让前台看一下你的身份证,他拿出信任名单进行比对,看到你的id在名单中,给你发一个临时出入证.你就可以把资料拿走了. 至于这个身份证是不是被本人所佩戴,(名单只对应身份证号,不对人).那就是接下来要操心的事了.