记一次spring boot升级跨域问题的坑

当前使用版本spring boot2.4.0,跨域配置如下:

package com.geostar.job.admin.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * @author 95844
 * 使用addCorsMappings(CorsRegistry registry)配置之后再使用自定义拦截器时跨域相关配置就会失效。
 * 原因是请求经过的先后顺序问题,当请求到来时会先进入拦截器中,而不是进入Mapping映射中,所以返回的头信息中并没有配置的跨域信息。浏览器就会报跨域异常。
 * 所以此处使用CorsFilter过滤器解决跨域问题
 */
@Configuration
public class CorsConfig {
    private CorsConfiguration corsConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
    /* 请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,只能是post方式等等)
    */
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setMaxAge(3600L);
        return corsConfiguration;
    }
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig());
        return new CorsFilter(source);
    }
}

我相信大家跟我一样,新建项目时这种配置类基本都是直接从老项目复制粘贴过来的,根本不会多想。

本地测试服务一切正常,部署到服务器几天了,自己通过swagger页面用了都没问题,今天前端才对接服务一上来就是跨域报错。。。。

这时就想了这跨域设置都在多少个项目上用了咋到这个不行了,经验主义告诉我不会出问题也没查日志啥的,因为本地服务玩了很多天了没见过什么报错信息。最后实在是找不到原因,看了下服务器运行日志,启动的时候果然报错了,尴尬至极!!!

然后赶紧灰溜溜的查看原因。

原来是因为升级后spring boot2.4.0对应的spring5.3.1的CorsFilter类针对CorsConfiguration新增了校验

源码如下:

/**
 * {@link javax.servlet.Filter} to handle CORS pre-flight requests and intercept
 * CORS simple and actual requests with a {@link CorsProcessor}, and to update
 * the response, e.g. with CORS response headers, based on the policy matched
 * through the provided {@link CorsConfigurationSource}.
 *
 * <p>This is an alternative to configuring CORS in the Spring MVC Java config
 * and the Spring MVC XML namespace. It is useful for applications depending
 * only on spring-web (not on spring-webmvc) or for security constraints that
 * require CORS checks to be performed at {@link javax.servlet.Filter} level.
 *
 * <p>This filter could be used in conjunction with {@link DelegatingFilterProxy}
 * in order to help with its initialization.
 *
 * @author Sebastien Deleuze
 * @since 4.2
 * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
 * @see UrlBasedCorsConfigurationSource
 */
public class CorsFilter extends OncePerRequestFilter {

	private final CorsConfigurationSource configSource;

	private CorsProcessor processor = new DefaultCorsProcessor();


	/**
	 * Constructor accepting a {@link CorsConfigurationSource} used by the filter
	 * to find the {@link CorsConfiguration} to use for each incoming request.
	 * @see UrlBasedCorsConfigurationSource
	 */
	public CorsFilter(CorsConfigurationSource configSource) {
		Assert.notNull(configSource, "CorsConfigurationSource must not be null");
		this.configSource = configSource;
	}


	/**
	 * Configure a custom {@link CorsProcessor} to use to apply the matched
	 * {@link CorsConfiguration} for a request.
	 * <p>By default {@link DefaultCorsProcessor} is used.
	 */
	public void setCorsProcessor(CorsProcessor processor) {
		Assert.notNull(processor, "CorsProcessor must not be null");
		this.processor = processor;
	}


	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
			FilterChain filterChain) throws ServletException, IOException {

		CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
		boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
		if (!isValid || CorsUtils.isPreFlightRequest(request)) {
			return;
		}
		filterChain.doFilter(request, response);
	}

}

类CorsFilter继承自OncePerRequestFilter,doFilterInternal方法会被执行。类中还创建了一个默认的处理类DefaultCorsProcessor,doFilterInternal调用this.processor.processRequest
往下看

processRequest——>handleInternal——>checkOrigin注意关键方法来了

	/**
	 * 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(@Nullable String requestOrigin) {
		if (!StringUtils.hasText(requestOrigin)) {
			return null;
		}
		if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
			if (this.allowedOrigins.contains(ALL)) {
				validateAllowCredentials();
				return ALL;
			}
			for (String allowedOrigin : this.allowedOrigins) {
				if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
					return requestOrigin;
				}
			}
		}
		if (!ObjectUtils.isEmpty(this.allowedOriginPatterns)) {
			for (OriginPattern p : this.allowedOriginPatterns) {
				if (p.getDeclaredPattern().equals(ALL) || p.getPattern().matcher(requestOrigin).matches()) {
					return requestOrigin;
				}
			}
		}
		return null;
	}

然后看到:validateAllowCredentials这个方法

	/**
	 * Validate that when {@link #setAllowCredentials allowCredentials} is true,
	 * {@link #setAllowedOrigins allowedOrigins} does not contain the special
	 * value {@code "*"} since in that case the "Access-Control-Allow-Origin"
	 * cannot be set to {@code "*"}.
	 * @throws IllegalArgumentException if the validation fails
	 * @since 5.3
	 */
	public void validateAllowCredentials() {
		if (this.allowCredentials == Boolean.TRUE &&
				this.allowedOrigins != null && this.allowedOrigins.contains(ALL)) {

			throw new IllegalArgumentException(
					"When allowCredentials is true, allowedOrigins cannot contain the special value \"*\"" +
							"since that cannot be set on the \"Access-Control-Allow-Origin\" response header. " +
							"To allow credentials to a set of origins, list them explicitly " +
							"or consider using \"allowedOriginPatterns\" instead.");
		}
	}

这就是刚才看到的报错信息,这里是说allowCredentials为true时并且allowedOrigins不为空且为ALL(这个ALL就是*)时就会抛出异常。

	/** Wildcard representing <em>all</em> origins, methods, or headers. */
	public static final String ALL = "*";

修改:

继续使用CorsFilter,使用官方推荐的allowedOriginPatterns即可,如下

package com.geostar.job.admin.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * @author 95844
 * 使用addCorsMappings(CorsRegistry registry)配置之后再使用自定义拦截器时跨域相关配置就会失效。
 * 原因是请求经过的先后顺序问题,当请求到来时会先进入拦截器中,而不是进入Mapping映射中,所以返回的头信息中并没有配置的跨域信息。浏览器就会报跨域异常。
 * 所以此处使用CorsFilter过滤器解决跨域问题
 */
@Configuration
public class CorsConfig {
    private CorsConfiguration corsConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
    /* 请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,只能是post方式等等)
    */
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setMaxAge(3600L);
        return corsConfiguration;
    }
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig());
        return new CorsFilter(source);
    }
}

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Boot在处理跨域请求时,通常是为了支持前端与后端分离的应用架构中,当客户端(如浏览器)发起 AJAX 请求到不同的域名或端口时,服务器默认会因为同源策略(Same-Origin Policy)而拒绝这些请求。为了解决这个问题,Spring Boot提供了几种方法来处理跨域。 1. **全局启用CORS**: 你可以通过`@EnableWebMvc`注解加上`spring.mvc.cross-origin.enabled=true`配置,然后在`application.properties`或`application.yml`文件中添加CORS相关配置,比如允许特定来源、方法和头信息: ```yaml spring: mvc: cors: enabled: true origins: '*' 或 'http://localhost:8080' // 允许特定或所有来源 allowedMethods: '*' // 允许的所有HTTP方法 allowedHeaders: '*' // 允许的所有请求头 ``` 2. **全局注册CORS Filter**: 使用`@CrossOrigin`注解可以全局注册一个CORS Filter,例如: ```java @Configuration @WebFilter(urlPatterns = "/*") public class CorsConfig implements WebFilterConfigurer { @Override public void configureWebFilter(WebFilterRegistry registry) throws Exception { registry.addFilter(CorsFilter.class).addMappingForAllUrls().applyPermitDefaultValues(); } } ``` 3. **控制器级别处理**: 如果只需要某个或部分Controller响应跨域,可以在方法上使用`@CrossOrigin`: ```java @RestController @CrossOrigin(origins = "*", methods = RequestMethod.GET) public class MyController { @GetMapping("/api") public String crossDomainApi() { // ... } } ``` 4. **自定义CORS策略**: 如果需要更灵活的控制,可以创建`CorsConfiguration`实例并动态配置: ```java @Bean public CorsConfigurationSource corsConfigurationSource() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); // ... 设置配置项 source.registerCorsConfiguration("/**", config); return source; } @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*"); } }; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值