天天看点

Cross-Origin Resource Sharing 跨域资源共享研究心得

现在的项目,要提供一个对外的接口给手机端浏览器访问,由于同域安全策略,浏览器会默认禁止js ajax对该跨域接口的访问,原本以为不能访问的。于是用chrome作为测试的浏览器,跨域访问该接口,发现实际上发送了一个http method为 options的请求到服务端,而请求中没有带上发送的body内容,反而有几个特殊的报文头添加到了http header中,

Request Method:OPTIONS

Request Headersview source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=.
Access-Control-Request-Headers:b, a, content-type
Access-Control-Request-Method:DELETE
Connection:keep-alive
Host:localhost:
Origin:http://.:
Referer:http://.:/ajax/test.html
User-Agent:Mozilla/ (Windows NT ; WOW64) AppleWebKit/ (KHTML, like Gecko) Chrome/. Safari/
           

通过在网上查找Access-Control-Request-Method ,发现了 Cross-Origin Resource Sharing 跨域资源共享,这个w3c 新标准,http://www.w3.org/TR/access-control

通过对该协议的了解了,发现,只要在options 请求的返回响应中,按照该标准进行验证,添加对应的resp header,

CORS最早的标准2008年发布,更新了多次,最新的标准是2014年发布,但是变化不大。andorid,ios浏览器都用的是开源 webkit核心,对这些标准的支持较好

分析该请求

request请求中多了

Access-Control-Request-Method:DELETE ,意思是,有一个DELETE http 请求申请通过

Access-Control-Request-Headers:b, a, content-type 意思是,该请求中带有不符合的 http标准的header头,b, a, content-type (在chrome 中有这个content-type,firefox却未出现,两浏览器准则有点不一样啊)

Cross-Origin Resource Sharing 跨域资源共享研究心得

通过CORS标准可以让该请求通过,

其中 Access-Control-Allow-Origin ,代表可以通过验证得域名

Access-Control-Allow-Credentials,true,false 允许携带用户认证凭据,(也就是是否允许cookie)

Access-Control-Allow-Methods , 允许通过的METHOD (DELETE , PUT,POST,GET,OPTIONS,)

Access-Control-Allow-Headers , 允许通过的requestHeader 用逗号隔开

Access-Control-Max-Age ,delta-seconds ,单位秒,该验证有效期限,超过期限,会重新发送(关闭chrome浏览器,重新打开该浏览器,也重发)

Access-Control-Expose-Headers,允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息(貌似webkit没有实现这个)

通过CORS 规范,在对应servlet中 的 doOptions 方法下,返回如下,就可以通过CORS验证

<!DOCTYPE html>
<html>
  <head>
    <title>test.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="jquery-2.1.3.js"></script>
    <script type="text/javascript">
        var _url= "http://10.8.186.36:8080/ajax/test";
        function sendHttp(){
            var url = _url+"?t="+new Date().getTime();
            $.ajax({
                url:url,
                method:"PUT",
                data:{a:},
                success:function(data){
                    document.getElementById("show").innerHTML = data.time;
                },
                error:function(){
                    document.getElementById("show").innerHTML = 'error';
                }
            })
        }
    </script>
  </head>

  <body>
    <form action="">
        <textarea id="send" rows="10" cols="50" id="test"></textarea> <br/>
        <input id="sendBtn" type="button" value="提交" onclick="sendHttp();"/>
        <input type="reset" value="重置" /><br/>
        <textarea id="show" rows="10" cols="50" ></textarea><br/>
    </form>
  </body>
</html>
           
package com;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet{

    private static final long serialVersionUID = L;

    public void doService(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
        Enumeration en = req.getHeaderNames();
        while(en.hasMoreElements()){
            String name = (String) en.nextElement();
            System.out.println(name+"ďźš" + req.getHeader(name));
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String time = sdf.format(Calendar.getInstance().getTime());
        String data = "{\"time\":"+time+"}";

        resp.addHeader("Access-Control-Allow-Origin", "*");
        resp.addHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE,HEAD");
        resp.addHeader("Access-Control-Allow-Headers", "a,b,content-type");
        resp.addHeader("Access-Control-Max-Age", "3600000");
        resp.addHeader("Access-Control-Allow-Credentials", "true");

        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().write(data);
        System.out.println("===================");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("get");
        doService(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("post");
        doService(req, resp);
    }


    @Override
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("Options");
        resp.addHeader("Access-Control-Allow-Origin", "*");
        resp.addHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE,HEAD");
        resp.addHeader("Access-Control-Allow-Headers", "a,b,content-type");
        resp.addHeader("Access-Control-Max-Age", "3600000");
        resp.addHeader("Access-Control-Allow-Credentials", "true");
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("put");
        doService(req, resp);
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("delete");
        doService(req, resp);
    }


    @Override
    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("head");
        doService(req, resp);
    }

}
           
Response Headersview source
Access-Control-Allow-Headers:a,b
Access-Control-Allow-Methods:POST,GET,OPTIONS,PUT,DELETE
Access-Control-Allow-Origin:*
Access-Control-Max-Age:
Content-Length:
Date:Fri,  Apr  :: GMT
Server:Apache-Coyote/
           

Access-Control-Allow-Headers:a,b 可以通过的自定义的头部 大小写不敏感

Access-Control-Allow-Methods:POST,GET,OPTIONS,PUT,DELETE 可以通过访问的HTTP方法

Access-Control-Allow-Origin:*

Access-Control-Max-Age:300 失效时间

options请求成功之后,浏览器判断服务器验证通过,会紧接着又触发了一次实际的请求,

其中 POST ,GET 并不会发送OPTIONS请求,

继续阅读