Spring @CrossOrigin 通配符 解决跨域问题
@CrossOrigin 通配符 解决跨域问题
痛点:
对很多api接口需要 开放H5 Ajax跨域请求支持 由于环境多套域名不同,而CrossOrigin 原生只支持* 或者具体域名的跨域支持 所以想让CrossOrigin 支持下通配 *.abc.com 支持所有origin 为 abc.com域(包括各种子域名)名来的Ajax 请求支持跨域.
解决思路:
支持通配
@CrossOrigin(origins = {"*.abc.com"}) 通配 主域+任意子域 www.abc.com order.api.abc.com dev.order.abc.com 等
@CrossOrigin(origins = {"*.order.abc.com"}) 通配order子域 子域名 dev.order.abc.com test.order.abc.com uat.order.abc.com 等
Spring 默认支持cors 拓展下 Spring 对跨域的处理类
解决方案:
获取 RequestMappingHandlerMapping 设置自定义 MyCorsProcessor 代替DefaultCorsProcessor
/**
* 给requestMappingHandlerMapping 对象注入自定义 MyCorsProcessor
* @author tomas
* @create 2019/8/12
**/
@Configuration
@EnableWebMvc
public class MyWebMvcConfig extends DelegatingWebMvcConfiguration {
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
handlerMapping.setCorsProcessor(new MyCorsProcessor());
return handlerMapping;
}
}
/**
* MyCorsProcessor 描述
* 自定义 如果xxx.com域下的请求允许跨域
*
* @author tomas
* @create 2019/8/12
**/
public class MyCorsProcessor extends DefaultCorsProcessor {
/**
* Check the origin of the request against the configured allowed origins.
* @param requestOrigin the origin to check
* @return the origin to use for the response, or {@code null} which
* means the request origin is not allowed
*/
@Nullable
public String checkOrigin(CorsConfiguration config, @Nullable String requestOrigin) {
if (!StringUtils.hasText(requestOrigin)) {
return null;
}
if (ObjectUtils.isEmpty(config.getAllowedOrigins())) {
return null;
}
if (config.getAllowedOrigins().contains(CorsConfiguration.ALL)) {
if (config.getAllowCredentials() != Boolean.TRUE) {
return CorsConfiguration.ALL;
}
else {
return requestOrigin;
}
}
AntPathMatcher pathMatcher = new AntPathMatcher("|");
for (String allowedOrigin :config.getAllowedOrigins()) {
if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
return requestOrigin;
}
//推荐方式:正则 注意(CrossOrigin(origins = {"*.abc.com"}) ) 主域会匹配主域+子域 origins = {"*.pay.abc.com"} 子域名只会匹配子域
if(pathMatcher.isPattern(allowedOrigin)&&pathMatcher.match(allowedOrigin,requestOrigin)){
return requestOrigin;
}
//不推荐方式:写死
if(allowedOrigin.contains("*.abc.com")&& requestOrigin.contains("abc.com")){
return requestOrigin;
}
}
return null;
}
}
原理分析:
Spring mvc cors
Spring MVC 的文档这样说:
Spring MVC 的 HandlerMapping 实现内置支持 CORS, 在成功映射一个请求到一个 handler 之后, HandlerMapping 会检查 CORS 配置以采取下一步动作。
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors-processing
Spring MVC 会在找到 handler 后通过添加一个拦截器来检查 CORS 配置。
下面来看一下 Spring MVC 中的 CORS 的实现。
DispatcherServlet 调用 AbstractHandlerMapping 中的 getHandler() 方法:
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
对于 Ajax 请求 getCorsHandlerExecutionChain 自动加上一个 CorsInterceptor 的拦截器:
/**
* Update the HandlerExecutionChain for CORS-related handling.
* <p>For pre-flight requests, the default implementation replaces the selected
* handler with a simple HttpRequestHandler that invokes the configured
* {@link #setCorsProcessor}.
* <p>For actual requests, the default implementation inserts a
* HandlerInterceptor that makes CORS-related checks and adds CORS headers.
* @param request the current request
* @param chain the handler chain
* @param config the applicable CORS configuration (possibly {@code null})
* @since 4.2
*/
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
if (CorsUtils.isPreFlightRequest(request)) {
HandlerInterceptor[] interceptors = chain.getInterceptors();
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
}
else {
chain.addInterceptor(new CorsInterceptor(config));
}
return chain;
}
AbstractHandlerMapping中 私有class CorsInterceptor
private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource {
@Nullable
private final CorsConfiguration config;
public CorsInterceptor(@Nullable CorsConfiguration config) {
this.config = config;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return corsProcessor.processRequest(this.config, request, response);
}
@Override
@Nullable
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return this.config;
}
}
CorsInterceptor中preHandle方法 实际处理 processRequest的是AbstractHandlerMapping.this.corsProcessor
这个corsProcessor =new DefaultCorsProcessor() 是一个默认的跨域处理类
我们的重点就是 重写DefaultCorsProcessor的checkOrigin 方法
@Override
@SuppressWarnings("resource")
public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
HttpServletResponse response) throws IOException {
if (!CorsUtils.isCorsRequest(request)) {
return true;
}
......
return handleInternal(serverRequest, serverResponse, config, preFlightRequest);
}
/**
* Handle the given request.
*/
protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
CorsConfiguration config, boolean preFlightRequest) throws IOException {
String requestOrigin = request.getHeaders().getOrigin();
String allowOrigin = checkOrigin(config, requestOrigin);
HttpHeaders responseHeaders = response.getHeaders();
responseHeaders.addAll(HttpHeaders.VARY, Arrays.asList(HttpHeaders.ORIGIN,
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));
if (allowOrigin == null) {
logger.debug("Rejecting CORS request because '" + requestOrigin + "' origin is not allowed");
rejectRequest(response);
return false;
}
..........
response.flush();
return true;
}
/**
* Check the origin and determine the origin for the response. The default
* implementation simply delegates to
* {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.
*/
// 重写此方法 支持通配符 或者支持正则表达式 写法见开头解决方案
@Nullable
protected String checkOrigin(CorsConfiguration config, @Nullable String requestOrigin) {
return config.checkOrigin(requestOrigin);
}
}
dispatcherServlet 中在真正 invoke handler 之前会先调用拦截器: 从而通过加的 cors 拦截器阻止请求。
doDispatch 方法:
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
注意问题:
- 如果您正在使用 Spring Security,请确保在 Spring 安全级别启用 CORS,并允许它利用 Spring MVC 级别定义的配置。在 Spring 安全级别启用 CORS:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
- 全局 CORS 配置
除了细粒度、基于注释的配置之外,您还可能需要定义一些全局 CORS 配置。这类似于使用筛选器,但可以声明为 Spring MVC 并结合细粒度 @CrossOrigin 配置。默认情况下,所有 origins and GET, HEAD and POST methods 是允许的。
使整个应用程序的 CORS 简化为:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
- 基于过滤器的 CORS 支持
作为上述其他方法的替代,Spring 框架还提供了 CorsFilter。在这种情况下,不用使用@CrossOrigin或``WebMvcConfigurer#addCorsMappings(CorsRegistry),,例如,可以在 Spring Boot 应用程序中声明如下的过滤器:
@Configuration
public class MyConfiguration {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
感谢 @大神张林峰老师 @王昆老师 @中睿老师 给出的宝贵意见
1、官方文档 https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
2、https://blog.csdn.net/weixin_33713503/article/details/88039675
https://www.jianshu.com/p/d05303d34222
https://www.cnblogs.com/helloz/p/10961039.html
2、https://blog.csdn.net/taiyangnimeide/article/details/78305131
3、https://blog.csdn.net/snowin1994/article/details/53035433
* * *
Spring @CrossOrigin 通配符 解决跨域问题的更多相关文章
- @CrossOrigin:解决跨域问题
注解@CrossOrigin解决跨域问题 阅读目录: 一.跨域(CORS)支持: 二.使用方法: 1.controller配置CORS 2.全局CORS配置 3.XML命名空间 4.How does ...
- 使用SpringMVC的@CrossOrigin注解解决跨域请求问题
跨域问题,通俗说就是用ajax请求其他站点的接口,浏览器默认是不允许的.同源策略(Same-orgin policy)限制了一个源(orgin)中加载脚本或脚本与来自其他源(orgin)中资源的交互方 ...
- spring @CrossOrigin解决跨域问题
阅读目录: 一.跨域(CORS)支持: 二.使用方法: 1.controller配置CORS 2.全局CORS配置 3.XML命名空间 4.How does it work? 5.基于过滤器的CORS ...
- 注解@CrossOrigin解决跨域问题
注解@CrossOrigin 出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源.例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上拥有EVILL网站.来自EVILL的脚本不能 ...
- Spring Boot中通过CORS解决跨域问题
今天和小伙伴们来聊一聊通过CORS解决跨域问题. 同源策略 很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不得不说说浏览器的同源策略. 同源策略是由Netscap ...
- Spring Boot 2.X 如何优雅的解决跨域问题?
一.什么是源和跨域 源(origin)就是协议.域名和端口号.URL由协议.域名.端口和路径组成,如果两个URL的协议.域名和端口全部相同,则表示他们同源.否则,只要协议.域名.端口有任何一个不同,就 ...
- Spring Boot2 系列教程(十四)CORS 解决跨域问题
今天和小伙伴们来聊一聊通过CORS解决跨域问题. 同源策略 很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不得不说说浏览器的同源策略. 同源策略是由 Netsca ...
- 提示"No 'Access-Control-Allow-Origin' header"及Spring 中解决跨域问题
问题描述 No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://12 ...
- spring boot 解决跨域访问
package com.newings.disaster.shelters.configuration; import org.springframework.context.annotation.B ...
随机推荐
- [06]ASP.NET Core中的进程内(InProcess)托管
ASP.NET Core 进程内(InProcess)托管 本文作者:梁桐铭- 微软最有价值专家(Microsoft MVP) 文章会随着版本进行更新,关注我获取最新版本 本文出自<从零开始学 ...
- 【学习笔记】动态规划—各种 DP 优化
[学习笔记]动态规划-各种 DP 优化 [大前言] 个人认为贪心,\(dp\) 是最难的,每次遇到题完全不知道该怎么办,看了题解后又瞬间恍然大悟(TAT).这篇文章也是花了我差不多一个月时间才全部完成 ...
- 杂牌机搞机之旅最终章————刷入Xposed框架
杂牌机搞机之旅最终章----刷入Xposed框架 recovery移植不成功,没办法,挂载分区好像挂载不上,所以,刷入magisk如果卡在开机屏,只能线刷解决..心累.. 所以,折腾完XPosed框架 ...
- NGINX 配置清单
以下内容来自 SimulatedGREG/nginx-cheatsheet. 通用设置 端口 listen server { # standard HTTP protocol listen 80; # ...
- ASP.NET中WebService的创建和部署以及通过反射动态调用WebService
一.在ASP.NET中创建WebService 首先我们先创建一个ASP.NET Web 应用程序,此处我们以VS2017为例 点击新创建的项目,右键添加新建项,选择Web服务,输入名称后点击添加 这 ...
- 为什么要学 Python? python该怎么学
很多童鞋对为什么学习Python感到迷茫,小编来跟大家说说学习Python的10个理由,希望可以帮助到大家!!! 摘要: 看完这十个理由,我决定买本python从入门到精通! 如果你定期关注现今的科技 ...
- XSS攻击(跨站脚本攻击)
一.什么是XSS?怎么发生的? XSS(Cross site scripting)全称为跨站脚本攻击,是web程序中最常见的漏洞.指攻击者在网页中嵌入客户端脚本(例如Javascript),当用户浏览 ...
- python web框架Django入门
Django 简介 背景及介绍 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以 ...
- FCC---Make a CSS Heartbeat using an Infinite Animation Count----超级好看的心跳,粉色的
Here's one more continuous animation example with the animation-iteration-count property that uses t ...
- 官宣:腾讯WeTest明星工具-PerfDog面向全球发布!
导读 PerfDog(官网:perfdog.qq.com)作为移动全平台性能测试分析专业工具,在腾讯内部研发测试工具商店-WeTest Store上线后服务了近2000+名开发者,其中<王者荣耀 ...