一.什么是CORS
CORS是解決瀏覽器跨域限制的W3C標准,詳見:https://www.w3.org/TR/cors/。
根據CORS標准的定義,在瀏覽器中訪問跨域資源時,需要做如下實現:
- 服務端在響應消息頭中包含消息頭:
Access-Control-Allow-Origin
,值為服務端允許訪問資源的域名稱,同時瀏覽器會根據該值與發起的請求消息頭Origin
值進行匹配,以確認服務端是否允許訪問跨域資源。 - 瀏覽器在發送非“簡單方法”(GET,HEAD請求被定義為簡單方法)之前,會發送一個預檢請求(通常是一個OPTIONS請求),瀏覽器根據響應消息頭驗證服務端是否允許訪問跨域資源,從而決定是否需要發送“實際請求”。
- 在服務端根據請求消息頭
Origin
值以決定是否允許瀏覽器訪問跨域資源,返回相應的消息頭。
具體來說,在實現時通常需要設置如下幾個響應消息頭:
Access-Control-Allow-Origin
:“origin-list” | “null” | “*”,允許訪問跨域資源的域名列表,對於預檢請求來說,決定是否會發送實際請求。Access-Control-Allow-Credentials
:true | false,表明實際請求中是否可以包含用戶憑證信息。Access-Control-Allow-Methods
:“method”,服務端允許訪問的實際請求方法名列表。Access-Control-Allow-Headers
:“field-name”,在“實際”請求中可以包含的消息頭名稱列表。Access-Control-Max-Age
:seconds,預檢請求結果緩存時間,單位:秒。在該時間范圍內,發送實際請求之前不再會發送預檢請求。
特別說明:對於需要跨域傳遞Cookie的場景,必須設置消息頭“Access-Control-Allow-Credentials”為“true”,且此時“Access-Control-Allow-Origin”值只能為某一指定單一域名。
簡而言之,CORS標准的核心就是:服務端需要在瀏覽器的跨域請求響應中包含指定消息頭,如下通過代碼示例說明。
二.實現CORS
在Serlvet中實現CORS
public class AjaxCorsServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(AjaxCorsServlet.class.getName());
@Override
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String origin = req.getHeader("Origin");
String allowMethod = req.getHeader("Access-Control-Request-Method");
String allowHeaders = req.getHeader("Access-Control-Request-Headers");
resp.setHeader("Access-Control-Allow-Origin", origin);// 允許指定域訪問跨域資源
resp.setHeader("Access-Control-Allow-Credentials", "true"); // 允許跨域傳遞Cookie
resp.setHeader("Access-Control-Max-Age", "86400"); // 瀏覽器緩存預檢請求結果時間,單位:秒
resp.setHeader("Access-Control-Allow-Methods", allowMethod);// 允許瀏覽器發送的實際請求方法名列表
resp.setHeader("Access-Control-Allow-Headers", allowHeaders);// 允許瀏覽器發送的請求消息頭
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
logger.info(String.format("ajax cors post do"));
String origin = req.getHeader("Origin");
resp.setHeader("Access-Control-Allow-Origin", origin); // 在實際請求中允許指定域訪問跨域資源
resp.setHeader("Access-Control-Allow-Credentials", "true"); // 允許跨域傳遞Cookie
resp.setHeader("Content-Type", "application/json"); // 服務端響應的數據類型
User user = new User();
user.setName("ajax cors post do");
user.setPwd("******");
resp.getWriter().write(JSON.toJSONString(user));
}
}
### 使用Spring框架 #### 在Spring 4.1.9.RELEASE及以下版本的解決方案 通過自定義Filter實現CORS,如下代碼說明: ```java public class CROSFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // to do something }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse)response;
String origin = req.getHeader("Origin");
resp.setHeader("Access-Control-Allow-Origin", origin); // 允許指定域訪問跨域資源
resp.setHeader("Access-Control-Allow-Credentials", "true"); // 允許跨域傳遞Cookie
if(RequestMethod.OPTIONS.toString().equals(req.getMethod())) {
String allowMethod = req.getHeader("Access-Control-Request-Method");
String allowHeaders = req.getHeader("Access-Control-Request-Headers");
resp.setHeader("Access-Control-Max-Age", "86400"); // 瀏覽器緩存預檢請求結果時間,單位:秒
resp.setHeader("Access-Control-Allow-Methods", allowMethod); // 允許瀏覽器發送的實際請求方法名列表
resp.setHeader("Access-Control-Allow-Headers", allowHeaders); // 允許瀏覽器發送的請求消息頭
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
// release resouces
}
}
Filter配置:
```xml
<filter>
<filter-name>CROSFilter</filter-name>
<filter-class>org.chench.springdemo.filter.CROSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CROSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在Spring 4.2.0.RC1及以上版本的解決方案
從Spring 4.2.0.RC1版本開始,Spring MVC提供了一個解決瀏覽器跨域限制的注解CrossOrigin
,只需要在Controller方法上使用該注解即可。
@RequestMapping(value="/post", method = {RequestMethod.POST})
@ResponseBody
@CrossOrigin(origin="*") // 使用CrossOrigin注解處理瀏覽器跨域限制問題
public User testAjaxCORSPost(HttpServletRequest req, HttpServletResponse resp,
@RequestBody User user) {
logger.info("ajax post do");
User u = new User();
u.setName("ajaxPost");
u.setPwd("111");
return u;
}
【參考】 https://spring.io/blog/2015/06/08/cors-support-in-spring-framework