前言
通过前两篇文章做好了的铺垫和讲述,现在的你应该了解了CORS是怎么回事以及Spring MVC对它是如何支持的,我有理由相信你现在完全是有能力去解决CORS跨域请求问题,而不用再是两眼一抹黑了。
正所谓好人做到底,送佛送到西,小伙伴一直最为关心Spring MVC对CORS的落地实操示例我还没有给出,当然还有它的处理流程原理分析,那么本文就是你最应该关注和收藏的了。
CORS跨域请求处理方式
针对CORS跨域请求的处理,了解了基础知识后的我们知道,即使没有Spring MVC的支持我们也是能够自行处理的,毕竟在Spring4.2之前都是开发者自己手动向HttpServletResponse设置请求头来解决问题的。
对于新时代的开发者,显然这种硬编码的方式就需要被淘汰el。Spring MVC内置的支持方式有多种,可谓非常多样和灵活。下面就聊聊这些处理方式并给出示例Demo,仅供参考。
方式一:自定义Filter/HandlerInterceptor
前面有说到,Spring直到4.2版本才提供了对CORS的支持,因此对于一些老项目,一般会使用自定义的Filter/拦截器来处理:
// 自定义一个Filter来处理CORS跨域请求 @Component public class CORSFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } // TODO:这里应该是只需要处理OPTIONS请求即可~~~ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "content-type,Authorization"); // response.setHeader("Access-Control-Allow-Credentials", "true"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
方式二:Nginx统一配置
配置在Nginx后,后端服务就不用再操心跨域请求问题了,这是很多公司推荐的方案。
此处我贴出一个配置供以参考,copy自这里
# # Wide-open CORS config for nginx # location / { #### 对OPTIONS请求,会设置很多的请求头,并返回204 if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Custom headers and headers various browsers *should* be OK with but aren't # add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; # # Tell client that this pre-flight info is valid for 20 days # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } }
上面是自定义方式解决,不强依赖于Spring MVC框架的支持。那么下面就是使用Spring4.2后提供的能力来灵活解决,这当然也是生厂上主流使用的方案。
方式三:CorsFilter
Spring MVC 4.2后内置了一个CorsFilter专门用于处理CORS请求问题,它所在的路径是:org.springframework.web.filter.CorsFilter。通过配置这个Filter使它生效便可统一控制跨域请求(URL级别控制):
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { ... // 使用javax.servlet.ServletContainerInitializer方式注册Filter @Override protected void registerDispatcherServlet(ServletContext servletContext) { super.registerDispatcherServlet(servletContext); // 注册Jar包内 内置的Filter等等 UrlBasedCorsConfigurationSource confiurationSource = new UrlBasedCorsConfigurationSource(); // 根据URL配置其对应的CORS配置 key支持的是AntPathMatcher.match() // 说明:这里请使用LinkedHashMap,确保URL的匹配顺序(/**请放在最后一个) Map<String, CorsConfiguration> corsConfigs = new LinkedHashMap<>(); //corsConfigs.put("*", new CorsConfiguration().applyPermitDefaultValues()); // '/**'表示匹配所有深度的路径 corsConfigs.put("/**", new CorsConfiguration().applyPermitDefaultValues()); confiurationSource.setCorsConfigurations(corsConfigs); // /*表示所有请求都用此filter处理一下 servletContext.addFilter("corsFilter", new CorsFilter(confiurationSource)) .addMappingForUrlPatterns((EnumSet.of(DispatcherType.REQUEST)), false, "/*"); } }
我觉得这个示例的难点反倒是注册这个Jar包内的Filter,若是SpringBoot
环境大伙都会注册,但本文示例是全注解驱动的Spring MVC
(木有web.xml
)环境。关于它的更多注册方式,可参见这里
配置好Filter后,点击发送按钮,即可正常跨域访问了。
方式四:@CrossOrigin
如果觉得使用CorsFilter
配置起来麻烦,或者你想实现精细化且更加简便的控制,那么@CrossOrigin
这个注解你值得拥有。
它使用方式极其简单,如下案例:
@CrossOrigin(origins = "http://localhost:63342", methods = {GET, POST, PUT, DELETE}, maxAge = 60L) @RequestMapping(value = "/test/cors", method = {OPTIONS, GET}) public Object testCors() { return "hello cors"; }
这样点击发送便能正常跨域请求了,截图如下:
难道每个Controller都显示的写上这个注解来处理?当然不是,除了这种局部配置外,Spring MVC还提供了下面这种全局配置的方式
方式五:WebMvcConfigurer方式全局配置
Spring MVC提供的这种配置方法我个人认为是最好的方式,能解决几乎所有问题。从语义配置上能表现出这是web层的东西而非其它,从使用上也非常的简单,因此我个人推荐这种方式而非Filter方式。
@Configuration @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/test/cors") // -------addMapping后还可以继续配置------- .allowedOrigins("http://localhost:63342") .maxAge(300L); registry.addMapping("/**").allowedOrigins("*"); } }
等价的xml的方式表达:
<mvc:cors> <mvc:mapping path="/test/cors" ... /> <mvc:mapping path="/**" ... /> </mvc:cors>
点击发送按钮当然也能正常work。截图如下:
本文我一共总结了5种方式来处理CORS的跨域访问问题,任意一种方式其实都可达到目的。此时你是否有这样一个疑问:若配置了多种方式(特别是Spring MVC内置的方式),生效的优先级顺序是怎样的呢?能够形成互补配置?
为了解答这个疑问,就应该先关注下Spring MVC它对CORS请求的一个处理流程以及配置初始化的过程。