原理:发生 跨域请求时,分二个请求,第一次请求叫 嗅探请求,prefight请求,也就是http OPTIONS请求,成功之后再进行真正的请求,这两次请求都是代码写的,浏览器不做options请求。
tomcat服务器需要做请求头返回和options请求处理:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws IOException, ServletException {
if (encoding == null) {
encoding = config.getInitParameter("encoding");
}
servletRequest.setCharacterEncoding(encoding);
servletResponse.setCharacterEncoding(encoding);
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
/**cors modified start**/
StringBuilder headers = new StringBuilder();
Enumeration<String> headerNames = request.getHeaders("Access-Control-Request-Headers");
if(Objects.nonNull(headerNames)) {
while (headerNames.hasMoreElements()) {
headers.append(headerNames.nextElement()).append(",");
}
}
response.setHeader("Access-Control-Allow-Headers", headers.toString());
/**cors modified end**/
if ("options".equalsIgnoreCase(request.getMethod())) {
response.setStatus(204);
} else {
chain.doFilter(request, response);
}
log.warn("url=" + ((HttpServletRequest)request).getRequestURL()+",method="+((HttpServletRequest)request).getMethod());
}
Nginx服务器请求处理:
server {
listen 9000 ssl;
listen [::]:9000 ssl;
root /usr/share/nginx/html;
ssl_certificate s1cert.pem;
ssl_certificate_key s1cert.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
add_header X-Frame-Options "ALLOW-FROM http://stand.alone.version/";
add_header Access-Control-Max-Age 3600;
# add_header X-Content-Type-Options nosniff;
location / {
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://172.19.0.2:80;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
prefight请求原理:
举个例子,一个客户端可能会在实际发送一个 DELETE
请求之前,先向服务器发起一个预检请求,用于询问服务器是否可以接收一个 DELETE 请求:
OPTIONS /resource/foo Access-Control-Request-Method: DELETE Access-Control-Request-Headers: origin, x-requested-with Origin: https://foo.bar.org
如果服务器允许,那么服务器就会响应这个预检请求。并且其响应首部 Access-Control-Allow-Methods 会将 DELETE
包含在其中:
HTTP/1.1 200 OK Content-Length: 0 Connection: keep-alive Access-Control-Allow-Origin: https://foo.bar.org Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE Access-Control-Max-Age: 86400
参考说明: