現在的項目,要提供一個對外的接口給手機端浏覽器通路,由于同域安全政策,浏覽器會預設禁止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卻未出現,兩浏覽器準則有點不一樣啊)
通過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請求,