JS跨域

跨域概念

跨域是浏览器遵循同源策略,对页面脚本的安全限制,不允许Javascript请求不同域的数据。

跨域的本质就是要解决同源策略的限制!假如有地址 http://www.example1.com:8080/a.jsp,如下请求是不符合同源策略限制的:

http://www.example1.com:8081/a.jsp     端口不一致
http://www.example2.com:8080/b.jsp     域名不一致
https://www.example1.com:8080/a.jsp    协议不一致

跨域之JSONP

JSONP跨域是利用<script>标签没有跨域限制的特性,通过其src属性模拟js文件加载,请求不同域的地址,返回符合Javascript语法的数据进行处理,从而间接实现跨域。

我们知道HTML中可以引入其他域的js、css等文件,JSONP正是利用这个特性,把src替换成我们要请求的不同域地址。服务器接收到请求后,把JSON数据包到回调函数中响应,实际上<script>的src属性仍旧加载了一段Javascript代码,但这段代码包含了我们服务器响应的数据。页面收到响应后可以像处理一般Javascript脚本一样,进行调用、处理。

JSONP原理示例

服务端接口:

1 @RequestMapping(value="/getUser", method=RequestMethod.GET)
2 @ResponseBody
3 public String getUser(String callback, Integer id) {
4     String json = "{'id':'123', 'name':'xlog2n'}";
5     String jsonp = callback + "(" + json + ")";
6     return jsonp;
7 }

前端请求:

1 <script type="text/javascript">
2     // 预先定义好回调函数
3     function handleUser(user) {
4         alert(user.id + ":" + user.name);
5     }
6 </script>
7 
8 <!-- 实际的JSONP是动态创建script标签 -->
9 <script type="text/javascript" src="http://xlog2n.com/getUser?callback=handleUser&id=123"></script>

实际使用jQuery进行JSONP请求时,<script>标签是动态创建的,<script>标签的src属性指定了不同域的/getUser接口,并且传递了callback指明回调函数的名称,当src加载完成后,实际是一段执行handleUser函数的Javascript代码,参数是从服务端响应回来的JSON数据,其实JSONP就是JSON + P(包装):

handleUser({'id':'123', 'name':'xlog2n'})

JSONP原理简单、兼容性好,jQuery发送JSONP请求也很方便,但是JSONP只支持GET方式的请求,对于POST跨域请求就无能为力了,究其原因<script>标签的src属性发出的请求和<img src=""/>一样都是GET请求,所以JSONP无法支持POST请求!

jQuery JSONP调用示例

jquery把jsonp跨域请求集成到了ajax方法中,jsonp参数指定了回调参数的key,以下示例中我们在后端可以使用 request.getParameter('callback')获取回调函数的名称:

1 $.ajax({
2     url: 'http://xlog2n.com/getUser',
3     dataType: 'jsonp',
4     jsonp: 'callback',
5     success: function(user) {
6         alert(user.id + ":" + user.name);
7     }
8 });

跨域之CORS

CORS是跨域资源共享标准,提供了一组HTTP头信息,用来描述服务器可以接受哪些来源、哪些方式的请求,从而保证了服务器可以进行跨域请求的控制,只接受符合要求的跨域请求。

服务端响应头设置

使用CORS跨域,只需要在反向代理如nginx中设置以下响应头(也可以程序中设置,如HttpServletResponse.setHeader方法)

 1 server {
 2     listen 80;
 3     server_name xlog2n.com;
 4     location / {
 5         add_header 'Access-Control-Allow-Origin' '*';
 6         add_header 'Access-Control-Allow-Methods' 'POST,GET,PUT,DELETE';
 7         add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,Cache-Control, ......';
 8         add_header 'Access-Control-Allow-Credentials' 'true';
 9         ......
10         ......
11         proxy_pass http://127.0.0.1:8080;
12     }
13     ......
14     ......
15 }

第一个头信息Access-Control-Allow-Origin表示服务端接受的跨域请求来源,星号表示接受任何来源,我们也可以指定一个域名,只接受该域名下的跨域请求。
第二个头信息Access-Control-Allow-Methods表示服务端接受的跨域请求方法,示例中只接受POST、GET、PUT、DELETE。
第三个头信息Access-Control-Allow-Headers表示服务端接受的跨域请求头信息,在需要对请求头进行访问控制时,设置该头信息。
第四个头信息Access-Control-Allow-Credentials表示服务端接受附带身份验证信息(Cookie)的跨域请求。要特别注意,如果设置该头信息为true,Access-Control-Allow-Origin不能设置为星号,否则附带身份验证信息的请求会失败

预测请求

大家可能注意到,有些跨域请求会多发起一个请求OPTIONS请求,其实这个OPTIONS请求就是预测请求。预测请求是浏览器主动发起的,用来测试服务端是否支持这个即将发起的跨域请求。并非所有的跨域请求都会先发送一个预测请求,预测请求是符合条件才触发的。

  • 当跨域请求是非GET请求时
  • 当跨域请求设置了Accept、Accept-Language(请自行百度会触发的所有头信息)
  • 当跨域请求设置了Content-Type头,但Content-Type值不为 application/x-www-form-urlencoded 或者 text/plain 或者 multipart/form-data 之一时

预测请求只有响应头,没有响应体。预测通过后实际的请求才会发起,也会收到相应的响应体。

CORS跨域POST请求示例

上文说到JSONP不支持POST请求,但CORS可以很方便的使用POST发送大量数据到后端:

 1 <html>
 2     <head>
 3         <script src="jquery.min.js" type="text/javascript"></script>
 4         <script src="jquery.json.min.js" type="text/javascript"></script>
 5     </head>
 6     <body>
 7         <button>TEST</button>
 8 
 9         <script type="text/javascript">
10             var orderInfo = { 
11                 itemId: '1600000000000000301',
12                 issueNo: 1,
13                 skuNum: 1,
14                 payPassword: '011455e004151e30fd1413da54e3d77d',
15                 balanceFee: 0,
16                 onlineFee: 100,
17                 jingBeanFee: 0,
18                 couponParams: [],
19                 mobile: '158****6060',
20                 activityType: 1,
21                 screen: '111',
22                 uuid: '78043a633e5a440bad90639c67225beb'
23             };
24 
25             $('button').on('click', function() {
26                 var body = $.toJSON(orderInfo);
27 
28                 /*
29                 // 方式一:使用原生JS发送Ajax POST跨域请求
30                 var xhr = new XMLHttpRequest();
31                 if(xhr) {
32                     xhr.open('POST', 'http://xlog2n.com/order', true);
33                     xhr.setRequestHeader('Content-Type', 'application/json');
34                     xhr.onreadystatechange = function() {
35                         if (xhr.readyState === 4) {
36                             if (xhr.status === 200) {
37                                 alert(xhr.responseText);
38                             } else{
39                                 alert('错误:' + xhr.status);
40                             }
41                         }        
42                     };
43                     xhr.send(body); 
44                 }
45                 */
46 
47                 // 方式二:使用jQuery发送Ajax POST跨域请求
48                 $.ajax({
49                     url: 'http://xlog2n.com/order',    
50                     type: 'post',
51                     dataType: 'JSON',
52                     data: body,
53                     headers: {'Content-Type':'application/json'},
54                     success: function(data) {
55                         alert(data);
56                     }
57                 });
58             });
59 
60         </script>
61     </body>
62 </html>

CORS是比较新的标准,所以各浏览器支持的情况不一样,尤其是那个什么6、7、8,但是CORS跨域支持POST等请求方法,可以很好的支持大数据的提交。和JSONP各有优劣,使用哪种方式还需您自己定夺!!!

IFrame跨域问题

未完待续...

 

posted @ 2017-07-12 00:04  哥特雪人  阅读(438)  评论(1)    收藏  举报