欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CORS filter解决ajax跨越问题解决及源码分析

程序员文章站 2022-07-10 10:18:34
...

CORS filter解决ajax跨越问题解决及源码分析

一看就知道什么问题,不知道的先了解一下跨域问题,AJAX跨域是个比较古老的问题,原来写过一篇文章说明解决跨域问题有四种方式解决本地开发接口请求跨域问题4种方案,今天加一种基于filter的方式来解决跨域。

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSO

NP只能发GET请求,CORS允许任何类型的请求。

定义:CORS其实出现时间不短了,它在*上的定义是:跨域资源共享(CORS)是一种网络浏览器的技术规范,它为We

b服务器定义了一种方式,允许网页从不同的域访问其资源。而这种访问是被同源策略所禁止的。CORS系统定义了一种浏览器和

服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安

全。而W3C的官方文档目前还是工作草案,但是正在朝着W3C推荐的方向前进。

   简言之,CORS就是为了让AJAX可以实现可控的跨域访问而生的

以往的解决方案:

  以前要实现跨域访问,可以通过JSONPFlash或者服务器中转的方式来实现,但是现在我们有了CORS

  CORSJSONP相比,无疑更为先进、方便和可靠。

    1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。

    2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

    3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS(这部分会在后文浏览器支持部分介绍)。


web.xml配置

 <!--CORS 跨域资源访问-->
  <filter>         
   <filter-name>CORS</filter-name>  
   <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>  
   <init-param>  
    <param-name>cors.allowOrigin</param-name>  
       <param-value>*</param-value>  
   </init-param>  
   <init-param>  
    <param-name>cors.supportedMethods</param-name>  
       <param-value>GET, POST</param-value>  
   </init-param>  
   <init-param>  
    <param-name>cors.supportedHeaders</param-name>  
       <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value>  
   </init-param>  
   <init-param>  
       <param-name>cors.exposedHeaders</param-name>  
       <param-value>Set-Cookie</param-value>  
   </init-param>  
   <init-param>  
       <param-name>cors.supportsCredentials</param-name>  
       <param-value>true</param-value>  
   </init-param>
  </filter>  
  <filter-mapping>
      <filter-name>CORS</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
    

jar引用

包含两个包

CORS filter解决ajax跨越问题解决及源码分析

http://central.maven.org/maven2/com/thetransactioncompany/cors-filter/2.6/cors-filter-2.6.jar

http://central.maven.org/maven2/com/thetransactioncompany/java-property-utils/1.9.1/java-property-utils-1.9.1.jar

maven引用

<!-- https://mvnrepository.com/artifact/com.thetransactioncompany/cors-filter -->
<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>cors-filter</artifactId>
    <version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.thetransactioncompany/java-property-utils -->
<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>java-property-utils</artifactId>
    <version>1.9.1</version>
</dependency>


前端代码

$.ajax("http://localhost:8080/test/service.do?key=test", {
    type: "POST",
    contentType: "application/json",
    data: '{\"header\":{\"key\":\"test\",\"resTime\":\"1401154450142\",\"reqSeq\":\"1\",\"channel\":\"1\",\"version\":\"\",\"sign\":\"8f22e4fb4326795909eadac4f433706c\"},\"body\":{\"pageSize\":\"10\",\"currentPage\":\"1\",}}'
,
    dataType: 'json',
    success: function(data, status, xhr) {
    	console.log(data);
    }
});

实验结果

CORS filter解决ajax跨越问题解决及源码分析


==========完美分割线=====================

那corsfilter 到底怎么做到的呢?原理是什么,源码是怎么写的?

我们一起来看一下

入口类 

com.thetransactioncompany.cors.CORSFilter

CORS filter解决ajax跨越问题解决及源码分析

CORSFilter实现Filter接口的方法,多余的方法getConfigure、setConfiguration,涉及到两个类

/**
	 * The CORS filer configuration.
	 */
	private CORSConfiguration config;


	/**
	 * Encapsulates the CORS request handling logic.
	 */
	private CORSRequestHandler handler;

Filter的初始化过程 首先执行init

@Override
	public void init(final FilterConfig filterConfig)
		throws ServletException {
                //从web.xml中读取配置项
		CORSConfigurationLoader configLoader = new CORSConfigurationLoader(filterConfig);

		try {
                       //设置配置文件
			setConfiguration(configLoader.load());

		} catch (CORSConfigurationException e) {

			throw new ServletException(e.getMessage(), e);
		}
	}


CORSCongurationLoader提供两种方式配置

1、web.xml

<init-param>  
    <param-name>cors.allowOrigin</param-name>  
       <param-value>*</param-value>  
   </init-param>  
   <init-param>  
    <param-name>cors.supportedMethods</param-name>  
       <param-value>GET, POST</param-value>  
   </init-param>  
   <init-param>  
    <param-name>cors.supportedHeaders</param-name>  
       <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value>  
   </init-param>  
   <init-param>  
       <param-name>cors.exposedHeaders</param-name>  
       <param-value>Set-Cookie</param-value>  
   </init-param>  
   <init-param>  
       <param-name>cors.supportsCredentials</param-name>  
       <param-value>true</param-value>  
   </init-param>

2、配置文件

cors.allowOrigin = http://example.com


匹配url进行跨域设置

主要完成以下工作

1、过滤URL

2、与配置文件比较

3返回结果Header封装

@Override
	public void doFilter(final ServletRequest request,
		             final ServletResponse response,
		             final FilterChain chain)
		throws IOException, ServletException {
                
		if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {

			// Cast to HTTP
			doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);

		} else {

			throw new ServletException("Cannot filter non-HTTP requests/responses");
		}
	}

private void doFilter(final HttpServletRequest request,
		              final HttpServletResponse response,
		              final FilterChain chain)
		throws IOException, ServletException {
                //配置文件比较
		CORSRequestType type = CORSRequestType.detect(request);

		// Tag if configured
		if (config.tagRequests)
			RequestTagger.tag(request, type);

		try {
			if (type.equals(CORSRequestType.ACTUAL)) {

				// Simple / actual CORS request
				handler.handleActualRequest(request, response);
                                //返回结果封装
				// Preserve CORS response headers on reset()
				CORSResponseWrapper responseWrapper = new CORSResponseWrapper(response);

				chain.doFilter(request, responseWrapper);

			} else if (type.equals(CORSRequestType.PREFLIGHT)) {

				// Preflight CORS request, handle but don't pass
				// further down the chain
				handler.handlePreflightRequest(request, response);

			} else if (config.allowGenericHttpRequests) {

				// Not a CORS request, but allow it through
				chain.doFilter(request, response);

			} else {

				// Generic HTTP requests denied
				printMessage(CORSException.GENERIC_HTTP_NOT_ALLOWED, response);
			}
		} catch (CORSException e) {

			printMessage(e, response);
		}
	}

进入 handler.handleActualRequest(request, response)查看
主要是和web.xml中的配置进行比对,并为response添加header参数

public void handleActualRequest(final HttpServletRequest request, 
		                        final HttpServletResponse response)
		throws CORSException {
	
		if (CORSRequestType.detect(request) != CORSRequestType.ACTUAL)
			throw CORSException.INVALID_ACTUAL_REQUEST;
		
		
		// Check origin against allow list
		Origin requestOrigin = new Origin(request.getHeader(HeaderName.ORIGIN));
		
		if (! config.isAllowedOrigin(requestOrigin))
			throw CORSException.ORIGIN_DENIED;
		
		
		// Check method
		final String method = request.getMethod().toUpperCase();
		
		if (! config.isSupportedMethod(method))
			throw CORSException.UNSUPPORTED_METHOD;
		
		
		// Success, append response headers
		if (config.supportsCredentials) {

			response.addHeader(HeaderName.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");

			// The string "*" cannot be used for a resource that supports credentials.
			response.addHeader(HeaderName.ACCESS_CONTROL_ALLOW_ORIGIN, requestOrigin.toString());

			// See https://bitbucket.org/thetransactioncompany/cors-filter/issue/16/
			response.addHeader(HeaderName.VARY, "Origin");

		} else {
			if (config.allowAnyOrigin) {
				response.addHeader(HeaderName.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
			} else {
				response.addHeader(HeaderName.ACCESS_CONTROL_ALLOW_ORIGIN, requestOrigin.toString());

				// See https://bitbucket.org/thetransactioncompany/cors-filter/issue/16/
				response.addHeader(HeaderName.VARY, "Origin");
			}
		}
		
		if (! exposedHeaders.isEmpty())
			response.addHeader(HeaderName.ACCESS_CONTROL_EXPOSE_HEADERS, exposedHeaders);
	}

进入 CORSResponseWrapper responseWrapper = new CORSResponseWrapper(response) 中CORSResponseWrapper发现重写了reset方法,将刚设置的header重新填入 response中去

CORS filter解决ajax跨越问题解决及源码分析

到此基本filter初始化——dofilter——配置参数比对——配置参数设置——response Header重写比较清晰了。

配置参数说明

  1. cors.supportedMethods取值范围{method-list},默认“GET, POST, HEAD, OPTIONS”。列举所支持的HTTP方法。该信息将通过“Access-Control-Allow-Methods”头信息返回给调用者,并且需要在service中实现CORS。非列表内的方法类型的请求将被CORS filters以HTTP 405 “Method not allowed”响应拒绝。
  2. cors.supportedHeaders取值范围{*|header-list},默认*。定义所支持的自定义请求头,其信息将通过“Access-Control-Allow-Headers”头信息返回给请求者。如果配置为*,则包含任何自定义请求头信息的请求都将被接受。CORS Filter对此的实现是简单打包请求全部信息返回给浏览器。自定义请求头是指由浏览器JavaScript应用通过XMLHttpRequest.setRequestHeader()方法。例如通知浏览器允许“Content-Type, X-Requested-With”请求头。
  3. cors.exposedHeaders取值{header-list},默认空表。列出浏览器通过XMLHttpRequest.getResponseHeader()方法可以暴露哪些header详细信息(而非简要信息)给跨域请求。CORS Filter通过“Access-Control-Expose-Headers”头提供这类信息详情,通知浏览器例如“X-Custom-1, X-Custom-2”自定义头信息可以安全的保留给初始化跨域请求的脚本。
  4. cors.supportsCredentials取值{true|false},默认true。提示所支持的用户凭据类型,如cookies、HTTP授权或客户端证书。CORS Filter利用该值构造“Access-Control-Allow-Credentials”头信息。
  5. cors.maxAge取值{int},默认-1(未定义)。定义web浏览器可以缓存预检请求结果的时间长度,单位为秒。如果值为-1,表示未定义。该信息通过“Access-Control-Max-Age”头信息传递给浏览器。建议浏览器保存预检请求缓存1小时,即该属性值为3600.
  6. cors.tagRequests取值{true|false},默认false(不标记,或没有标签)。允许HTTP servlet请求标记提供给下游处理程序的CORS信息。允许标记只需将该属性值配置为true。


参考资料 

http://software.dzhuvinov.com/cors-filter-configuration.html

CORS filter解决ajax跨越问题解决及源码分析

CORS filter解决ajax跨越问题解决及源码分析

欢迎关注讨论
相关标签: java 跨域