跨域访问-预请求及跨域常见问题

来源:互联网 发布:淘宝怎么改登录名 编辑:程序博客网 时间:2024/04/28 20:58

预请求

参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#预请求

简而言之,在跨域并且尝试添加一些特殊头及自定义头的情况下,由于浏览器的安全机制,会加多一次OPTIONS预请求(询问请求),与跨域服务器协商可以设置的头部信息,可以允许的HTTP协议等等信息。

以如下图一次跨域请求为例。

图中代码如下

 1 var settings = { 2     type: "POST", 3     url: 'http://www.movesun.com/cors.php?allow_method=PUT', 4     contentType: "application/json", 5     dataType:"json", 6     data : { 7         "name" : "lvyahui" 8     }, 9     xhrFields : {10         // withCredentials : true11     },12     success: function(resp) {13         console.log(resp);14     }15     ,16     headers: {17         appkey:"87a8ea08-dbaa-11e6-b3f9-7056818a4db5",18         "X_forwarded-for":"10.104.239.XXX"19     }20 };21 $.ajax(settings);

可以看到,这段代码在movesun.com网站下,尝试向www.movesun.com发送跨域POST 请求,并且有自定义头(Content-Type设置了application/json类型也是原因之一),因此浏览器在发送真实post请求之前,发起了一个OPTIONS请求询问。

请求之所以可以成功,是因为后端服务器正常处理了OPTIONS请求,并且响应了正确的跨域响应头,后端代码cors.php如下

 1 <?php 2  3 if('OPTIONS' ===  $_SERVER['REQUEST_METHOD']){ 4     header('Access-Control-Allow-Origin:*'); 5     header('Access-Control-Allow-Headers:appkey,X_forwarded-for,Content-Type'); 6     header('Access-Control-Allow-Methods:' . (isset($_GET['allow_method']) ? $_GET['allow_method'] : 'OPTIONS')); 7     //header('Access-Control-Allow-Credentials:true'); 8     exit(0); 9 }10 11 header('Access-Control-Allow-Origin:*');12 13 echo json_encode(array(14     'code'  =>  0,15     'msg'   =>  '',16     'data'  =>  array()17 ));exit(0);

可以看到服务器判断请求类型为OPTIONS时,指定了如下几个Http响应头

1、Access-Control-Allow-Origin  : 跨域服务器允许的来源地址(跟请求的Origin进行匹配),可以是*或者某个确切的地址,不允许多个地址。当然后台代码可以动态判断来源地址进行动态设置,这主要是因为有时需要允许任意来源访问,并且要携带Cookie,此时需要明确指定地址(原因在文后常见问题中说明),下面这段PHP代码和Java代码(注意Java代码中Cookie没有取端口,因为Cookie端口不同也算同域,可以访问到)就是取来源地址并响应

 1 if (isset($_SERVER['HTTP_REFERER'])) { 2     $urls = parse_url($_SERVER['HTTP_REFERER']); 3     $url = $urls['scheme'] . '://' . $urls['host']; 4     if (isset($urls['port'])) { 5         $url .= ':' . $urls['port']; 6     } 7 } else { 8     $url = '*'; 9 }10 11 header("Access-Control-Allow-Origin: " . $url);
 1 public void filter(ContainerRequestContext requestContext) throws IOException { 2     String origin = requestContext.getHeaderString("Origin"); 3     if(origin != null && !origin.trim().equals("") 4             // postMan 请求的protocol 是 chrome-extension:// 5             && origin.startsWith("http://")){ 6         URL url  = new URL(origin); 7         String strUrl = url.getProtocol() + "://" + url.getHost(); 8         if(url.getPort() > 0){ 9             strUrl += ":" + url.getPort();10         }11         originUrl = strUrl;12         if(!cookieDomainAuto13                 && (sysConfig.getCookieDomain() == null || sysConfig.getCookieDomain().equals(""))){14             cookieDomainAuto = true;15         }16         if(cookieDomainAuto){17             // 动态判断 cookie domain18             if(url.getHost().matches(PATTERN_IP)){19                 // IP20                 sysConfig.setCookieDomain(url.getHost());21             } else {22                 int start = url.getHost().lastIndexOf('.',url.getHost().lastIndexOf('.') - 1);23                 String domain;24                 if(start > 0){25                     domain = url.getHost().substring(start + 1);26                 }else{27                     domain = url.getHost();28                 }29                 // domain30                 sysConfig.setCookieDomain(domain);31             }32         }33     }34 }
Java动态设置Allow-Origin与Cookie Domain

2、Access-Control-Allow-Methods:跨域服务器允许的请求方法。经测试发现,不论Access-Control-Allow-Methods设置为简单请求还是复杂请求类型,所有的简单的请求(GET,HEAD,POST)也是可以正常请求的。

3、Access-Control-Allow-Headers:跨域服务器允许客户端添加或自定义哪些http 头。

下面是这两次请求的报文

OPTIONS请求报文

 1 OPTIONS http://www.movesun.com/cors.php HTTP/1.1 2 Host: www.movesun.com 3 Proxy-Connection: keep-alive 4 Pragma: no-cache 5 Cache-Control: no-cache 6 Access-Control-Request-Method: POST 7 Origin: http://movesun.com 8 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 9 Access-Control-Request-Headers: appkey, content-type, x_forwarded-for10 Accept: */*11 Referer: http://movesun.com/12 Accept-Encoding: gzip, deflate, sdch13 Accept-Language: zh-CN,zh;q=0.814 15 16 HTTP/1.1 200 OK17 Date: Fri, 10 Mar 2017 05:48:07 GMT18 Server: Apache19 Access-Control-Allow-Origin: *20 Access-Control-Allow-Headers: appkey,X_forwarded-for,Content-Type21 Access-Control-Allow-Methods: POST22 Vary: User-Agent,Accept-Encoding23 Content-Encoding: gzip24 Content-Length: 2025 Content-Type: text/html26 X-Cache: MISS from SK-SQUIDDEV-1127 X-Cache-Lookup: MISS from SK-SQUIDDEV-11:8080

POST请求报文

 1 POST http://www.movesun.com/cors.php HTTP/1.1 2 Host: www.movesun.com 3 Proxy-Connection: keep-alive 4 Content-Length: 12 5 Pragma: no-cache 6 Cache-Control: no-cache 7 Origin: http://movesun.com 8 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 9 Content-Type: application/json10 Accept: application/json, text/javascript, */*; q=0.0111 X_forwarded-for: 10.104.239.19412 appkey: 87a8ea08-dbaa-11e6-b3f9-7056818a4db513 Referer: http://movesun.com/14 Accept-Encoding: gzip, deflate15 Accept-Language: zh-CN,zh;q=0.816 name=lvyahui

从报文中可以看出,OPTIONS请求后台可以拿到URL中的GET参数,也就是说,如果真实请求是GET请求,则后端在处理来询问的OPTIONS请求时,就可以获取到所有查询参数了。如mozilla官网所写,笔者调试发现,一些跨域请求,即便抛出了错误的情况下,请求也真的到了后台服务器,只是响应被浏览器拦截了。

另外,有时不想在后台代码中处理OPTIONS请求,则可以在nginx server节点下做如下配置,表示拦截处理所有OPTIONS请求。

 1 location ^~ / { 2     if ($request_method = OPTIONS ) { 3         add_header Content-Length 0; 4         add_header Content-Type text/plain; 5         add_header 'Access-Control-Allow-Origin' '*'; 6         add_header 'Access-Control-Allow-Methods' '*'; 7         add_header 'Access-Control-Allow-Headers' 'appkey,X_forwarded-for,Content-Type'; 8         return 200; 9     }10 }

 

常见跨域问题

下面是一些跨域下常见的一些问题

  • 添加了跨域服务器不允许的自定义头会抛出 XMLHttpRequest cannot load http://www.movesun.com/cors.php. Request header field custom_heaer is not allowed by Access-Control-Allow-Headers in preflight response.
  • 当未设置允许某种复杂请求时,使用复杂请求就会抛出如下错误,表示真实请求使用了服务器不允许的方法。在只允许POST的情况下,GET请求是可以被发送的,HEAD也可以成功,仅仅允许GET的情况下,POST也是可以发送成功的,HEAD也可以成功 。简单请求都可以成功,等等,其实经测试发现,不论Access-Control-Allow-Methods设置为简单请求还是复杂请求类型,所有的简单的请求(GET,HEAD,POST)也是可以正常请求的。XMLHttpRequest cannot load http://www.movesun.com/cors.php. Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
  • 预处理请求没有没正常处理,这种是询问请求响应了非200状态码,会抛出 XMLHttpRequest cannot load http://movesun.qq.com/cors.php?allow_method=PUT. Response for preflight has invalid HTTP status code 405 。如  
  • 1 if('OPTIONS' ===  $_SERVER['REQUEST_METHOD']){2     header("HTTP/1.1 405 Method Not Allowed");3     exit(-1);4 }

     

  • 错误是来源地址不是服务器所允许的来源地址。如下,此时服务器响应 Access-Control-Allow-Origin:http://www.movesun.com,表示跨域服务器允许在Origin:http://www.movesun.com 的机器上访问,而用户试图在http://movesun.com跨域请求目的服务器http://movesun.qq.com/cors.php?allow_method=PUT:XMLHttpRequest cannot load http://movesun.qq.com/cors.php?allow_method=PUT. Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'http://www.movesun.com' that is not equal to the supplied origin. Origin 'http://movesun.com' is therefore not allowed access.
  • 前端设置了携带签名标志,但是跨域服务器不允许携带,没有设置 Access-Control-Allow-Credentials:true 。如:XMLHttpRequest cannot load http://movesun.qq.com/cors.php?allow_method=PUT. Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin 'http://movesun.com' is therefore not allowed access.
  • 前端尝试在真实请求中携带签名Cookie,跨域服务器允许携带Cookie,但是服务器允许所有来源地址,会报这个错误,在跨域携带cookie时,必须明确指定来源地址,比如 Access-Control-Allow-Origin:http://movesun.com。例如:XMLHttpRequest cannot load http://movesun.qq.com/cors.php?allow_method=PUT. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'http://movesun.com' is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute.
并且 跨域携带Cookie时,跨域服务器处理询问请求(OPTIONS)和真实请求,都必须响应明确的来源地址和允许携带cookie的标志。否则会报上面两种错误。当然很显然的两次响应的Allow-Origin都是一致的。如下
 1 if('OPTIONS' ===  $_SERVER['REQUEST_METHOD']){ 2     header('Access-Control-Allow-Origin:http://movesun.com'); 3     header('Access-Control-Allow-Headers:appkey,X_forwarded-for,Content-Type'); 4     header('Access-Control-Allow-Methods:' . (isset($_GET['allow_method']) ? $_GET['allow_method'] : 'OPTIONS')); 5     header('Access-Control-Allow-Credentials:true'); 6     exit(0); 7 } 8 header('Access-Control-Allow-Origin:http://movesun.com'); 9 header('Access-Control-Allow-Credentials:true');10 echo json_encode(array(11     'code'  =>  0,12     'msg'   =>  '',13     'data'  =>  array()14 ));exit(0);

 

注意:文中的测试接口 在 http://movesun.com/cors.php  或者 http://www.movesun.com/cors.php,感兴趣的读者可以用这个接口测试。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 包裹显示待收件人向海关申报怎么办 在越南签证被公安扣了怎么办 酷派手机收不到验证码怎么办 苹果想把图片上的字盖上怎么办 婴儿自己把眼珠子抠红了怎么办 如果美陆战队员进入台湾那怎么办? 顺产生完小孩吸不通奶怎么办 耐克空军一号白色底发黄怎么办 中行网银u盾丢了怎么办 有人用你的手机号码不停注册怎么办 获得公开你微信头像的权限是怎么办 手机能进的网站电脑进不去怎么办 苹果8p下不了微信怎么办 苹果手机版本过底不能下微信怎么办 手机打开视频跳转到qq是怎么办 淘宝店铺显示服务竟然出错了怎么办 母羊下完羊羔把羊衣吃了怎么办? 移植后56天有黑色东西怎么办 我家的金丝熊浑身都是尿怎么办 一键启动车钥匙丢了怎么办 把爷爷的遗物弄丢了怎么办 如果你娶了一个傻子你怎么办 在国外订机票手机收不到信息怎么办 网上买机票名字写错了怎么办 买机票名字错了一个字怎么办 微店没收到货却显示已收货怎么办? 手机存的照片误删了怎么办 魔兽世界把要用的装备分解了怎么办 邻居家的狗见到我就叫怎么办 我的世界玩的时间长会卡应该怎么办 网易我的世界密码账号都忘了怎么办 我的世界创建世界画面乱码了怎么办 网易我的世界云端存档不够用怎么办 玩刺激战场带耳机声音有延迟怎么办 我的世界手机版狼变色怎么办 我的世界开了光影太阳太刺眼怎么办 我的世界饥饿值掉的慢怎么办 我的世界合装备过于昂贵怎么办 我的世界故事模式屏幕是黑的怎么办 人物只剩下轮廓的图用ps怎么办 两年义务兵考军校分数不够怎么办