CORS问题详解

参考:http://restlet.com/company/blog/2015/12/15/understanding-and-using-cors/
https://stackoverflow.com/questions/27915191/how-does-the-chrome-browser-decide-when-to-send-options

什么是CORS

Cross Origin Resource Sharing(CORS),顾名思义,即跨域共享,当两个不同domain进行访问的时候,默认情况是不能访问的,需要解决CORS问题。

理解CORS两种不同请求

在远程web应用决定一个request是否能被服务的时候,CORS会区分两种请求:
(1)Simple requests :如果远程服务器允许当前发送域发送请求,简单请求就会直接返回结果,否则会返回权限错误
(2)Preflighted requests:先发送一个和target request一样的url的OPTIONS请求去检查是否有权利执行这个请求,OPTIONS如果正常处理了就会返回什么类型的请求可以被执行,浏览器就会匹配当前请求,如果可以被执行,就会接着发送真正的target请求。举例:
当我们向请求头添加signkey、appid、token等字段的时候,浏览器首先发送一个option请求,然后根据服务器的设置请求返回如下字样:
这里写图片描述
可以看到允许header的内容包括signkey、appid、token等,所以浏览器就第二次发送请求:
这里写图片描述
这样Preflighted请求就正常发送了

两种请求发送时机

如果满足下面的情况,跨域算法就会发送简单请求:

  • request method是simple method 并且没有设置force preflight flag(这个怎么设置暂时还没找到)
  • request header是simple header或者request header为空
    否则,就会发送preflight request

simple method以及simple header

1、simple method(区分大小写)
- GET
- HEAD
- POST
2、simple header(区分大小写)
- header字段包括Accept, Accept-Language, 或者 Content-Language
- header字段包括Content-Type并且Content-Type值为 application/x-www-form-urlencoded, multipart/form-data, 或者 text/plain.

常见服务器处理跨域问题

各个平台跨域链接:https://enable-cors.org/
没有仔细看,就试了几个:
(1)springMVC跨域:参见http://blog.csdn.net/isea533/article/details/50449907
(2)springBoot跨域:
添加一个配置类,继承WebMvcConfigurerAdapter
重写addCorsMappings方法:

@Configuration
public class CorsConfiguration extends WebMvcConfigurerAdapter {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  //允许跨域访问的链接 "/**" 表示允许所有链接
                .allowedMethods("*");           //允许的http方法(GET,PUT,POST,DELETE...),"*"表示允许所有方法
    }
}

(3)jersey跨域:http://www.codingpedia.org/ama/how-to-add-cors-support-on-the-server-side-in-java-with-jersey/
先写Provider类(我的方法名是CrosFliter有点问题,应该是CrosPorvider比较合理)

@Provider
public class CORSResponseFilter
implements ContainerResponseFilter {

    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
            throws IOException {

        MultivaluedMap<String, Object> headers = MultivaluedMap<String, Object> headers = responseContext.getHeaders();

        headers.add("Access-Control-Allow-Origin", "*");
        headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT,OPTIONS");
        headers.add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Codingpedia,signkey,appid,token");
    }

}

然后注册这个Provider,常见注册方法见这个:https://blog.dejavu.sk/2013/11/19/registering-resources-and-providers-in-jersey-2/
我使用的是在web.xml注册:

<servlet>
        <servlet-name>MobileJerseyServlet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.xx.xx.mobile.rest</param-value>
        </init-param>
        <init-param>
            <param-name>jersey.config.server.provider.scanning.recursive</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>jersey.config.server.provider.classnames</param-name>
            <param-value>com.xx.xx.filter.CrosFilter</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>MobileJerseyServlet</servlet-name>
        <url-pattern>/api/ams/mobile/*</url-pattern>
    </servlet-mapping>
</servlet>

可以看到,在我的CORSResponseFilter类中,为了能接收OPTIONS请求,我在Access-Control-Allow-Methods加了OPTIONS,然后为了能够接收header有signkey、appid、token等特殊字段的请求,我也添加了允许。
最后,因为OPTIONS请求是不会携带一些特殊数据比如token信息的,所以如果你的服务器有对这个信息的校验的时候,需要忽略OPTIONS请求,否则OPTIONS请求被判为不合法:

 if (!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
         //过滤处理的代码
            }
        }else{
            chain.doFilter(request,response);
        }

猜你喜欢

转载自blog.csdn.net/u014473112/article/details/78933355
今日推荐