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

本文介绍SpringBoot 2.4.0版本中跨域配置的常见错误及解决办法,特别是针对使用CorsFilter时出现的问题。由于版本更新引入的新特性导致原有配置失效,文章提供了详细的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当前使用版本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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值