SpringBoot在使用SpringSecurity时配置跨域过滤器CorsFilter不生效
- 此文中代码只粘贴部分代码,完整版请自行查看
- 请求一般为重启debug服务再次请求
1.配置
一般配置方法(适用于没有SpringSecurity配置时)
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
List<String> list = Arrays.asList("*");
corsConfiguration.setAllowedHeaders(list);
corsConfiguration.setAllowedMethods(list);
corsConfiguration.setAllowedOrigins(list);
source.registerCorsConfiguration("/**", corsConfiguration);
CorsFilter corsFilter = new CorsFilter(source);
return corsFilter;
}
}
SpringSecurity配置时配置方法
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilterBean() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
List<String> list = Arrays.asList("*");
corsConfiguration.setAllowedHeaders(list);
corsConfiguration.setAllowedMethods(list);
corsConfiguration.setAllowedOrigins(list);
source.registerCorsConfiguration("/**", corsConfiguration);
CorsFilter corsFilter = new CorsFilter(source);
FilterRegistrationBean<CorsFilter> filterRegistrationBean = new FilterRegistrationBean<>(corsFilter);
filterRegistrationBean.setOrder(-101);
return filterRegistrationBean;
}
}
2.原因分析(过滤器有加载顺序)
首先说明Spring中过滤器加载使用的是FilterRegistrationBean配置过滤器,FilterRegistrationBean实现了Ordered接口,order排序值默认值为
Ordered.LOWEST_PRECEDENCE=Integer.MAX_VALUE
一般配置时的问题
- 如下代码为SpringSecurity的过滤器注册bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class})
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
}
代码中DEFAULT_FILTER_NAME=“springSecurityFilterChain”,securityProperties.getFilter().getOrder()=-100
- CorsFilter的过滤器注册bean由Spring生成,而生成的order=(Ordered.LOWEST_PRECEDENCE=Integer.MAX_VALUE),且优先按照Ordered接口排序,
@Order注解在没有实现Ordered接口时才生效
3.问题查找过程
a.检查CorsFilter是否执行
查看CorsFilter类可以知道跨域逻辑主要在以下doFilterInternal()方法执行,在此方法添加断点,debug执行,发送请求时未执行此方法,由此判断CorsFilter未执行过滤逻辑
public class CorsFilter extends OncePerRequestFilter {
@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);
}
}
b.检查Filter链
找到FilterChain接口,其中有如下doFilter()方法,在此方法添加断点
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
请求时断点停顿处为ApplicationFilterChain,在此类中找到属性ApplicationFilterConfig[] filters,查看filters的值如图
ApplicationFilterChain的方法internalDoFilter()逻辑可以看出filters执行顺序为数组下标升序,而corsFilter位于springSecurityFilterChain的后面,
当执行到springSecurityFilterChain时,springSecurityFilterChain过滤器内部会进行身份校验获取用户信息,如果是跨域请求,用户验证信息会被浏览器拦截,直接中断过滤器执行,
而corsFilter的跨域配置还没有执行到,所以需要配置corsFilter的执行顺序在springSecurityFilterChain之前
public final class ApplicationFilterChain implements FilterChain {
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if (Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
}
}
c.分析filter链的生成过程
ApplicationFilterChain中filters过滤连的赋值操作,在addFilter()方法中进行,在此方法中添加断点
public final class ApplicationFilterChain implements FilterChain {
void addFilter(ApplicationFilterConfig filterConfig) {
filters[n++] = filterConfig;
}
}
请求时可得到如下调用栈
查看ApplicationFilterFactory类的方法createFilterChain()可知,ApplicationFilterConfig由FilterMap生成,而FilterMaps[]
由context(TomcatEmbeddedContext)的findFilterMaps()获取到
public final class ApplicationFilterFactory {
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
// Acquire the information we will need to match filter mappings
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null) {
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMap, requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
}
}
进入findFilterMaps()(此方法为TomcatEmbeddedContext继承自StandardContext的)处在StandardContext类中,分析可知FilterMap[]
由filterMaps生成,而filterMaps由addFilterMapBefore()和addFilterMap()方法添加,在两个方法添加断点
public class StandardContext extends ContainerBase implements Context, NotificationEmitter {
@Override
public FilterMap[] findFilterMaps() {
return filterMaps.asArray();
}
@Override
public void addFilterMapBefore(FilterMap filterMap) {
validateFilterMap(filterMap);
// Add this filter mapping to our registered set
filterMaps.addBefore(filterMap);
fireContainerEvent("addFilterMap", filterMap);
}
@Override
public void addFilterMap(FilterMap filterMap) {
validateFilterMap(filterMap);
// Add this filter mapping to our registered set
filterMaps.add(filterMap);
fireContainerEvent("addFilterMap", filterMap);
}
}
请求时断点停顿处,其调用栈如下
进入addMappingForUrlPatterns()方法,分析FilterMap由filterDef生成,而filterDef由setInitParameter()方法和ApplicationFilterRegistration()
构造方法控制,在方法添加断点
public class ApplicationFilterRegistration
implements FilterRegistration.Dynamic {
public ApplicationFilterRegistration(FilterDef filterDef,
Context context) {
this.filterDef = filterDef;
this.context = context;
}
@Override
public void addMappingForUrlPatterns(
EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
String... urlPatterns) {
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(filterDef.getFilterName());
if (dispatcherTypes != null) {
for (DispatcherType dispatcherType : dispatcherTypes) {
filterMap.setDispatcher(dispatcherType.name());
}
}
if (urlPatterns != null) {
// % decoded (if necessary) using UTF-8
for (String urlPattern : urlPatterns) {
filterMap.addURLPattern(urlPattern);
}
if (isMatchAfter) {
context.addFilterMap(filterMap);
} else {
context.addFilterMapBefore(filterMap);
}
}
// else error?
}
@Override
public boolean setInitParameter(String name, String value) {
if (name == null || value == null) {
throw new IllegalArgumentException(
sm.getString("applicationFilterRegistration.nullInitParam",
name, value));
}
if (getInitParameter(name) != null) {
return false;
}
filterDef.addInitParameter(name, value);
return true;
}
}
请求时断点停顿,调用栈为
进入addFilter()方法,使用evaluate expression计算器分析context.findFilterDef(filterName)为null,filterDef为filterName赋值
public class ApplicationContext implements ServletContext {
private FilterRegistration.Dynamic addFilter(String filterName,
String filterClass, Filter filter) throws IllegalStateException {
FilterDef filterDef = context.findFilterDef(filterName);
// Assume a 'complete' FilterRegistration is one that has a class and
// a name
if (filterDef == null) {
filterDef = new FilterDef();
filterDef.setFilterName(filterName);
context.addFilterDef(filterDef);
} else {
if (filterDef.getFilterName() != null &&
filterDef.getFilterClass() != null) {
return null;
}
}
return new ApplicationFilterRegistration(filterDef, context);
}
}
再次查看调用栈,分析可知filterName来源于addRegistration()方法
public abstract class AbstractFilterRegistrationBean<T extends Filter> extends DynamicRegistrationBean<Dynamic> {
@Override
protected Dynamic addRegistration(String description, ServletContext servletContext) {
Filter filter = getFilter();
return servletContext.addFilter(getOrDeduceName(filter), filter);
}
}
进入addRegistration()方法分析知道filter来自getFilter()
,且this类型为FilterRegistrationBean,进入FilterRegistrationBean的getFilter()方法,返回为this.filter,查找filter的赋值操作setFilter()
方法和FilterRegistrationBean()构造方法,添加断点
public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {
@Override
public T getFilter() {
return this.filter;
}
public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) {
super(servletRegistrationBeans);
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
}
public void setFilter(T filter) {
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
}
}
请求时断点停顿,分析调用栈找到FilterRegistrationBean的filter赋值操作的源头为addAsRegistrationBean()方法
进入addAsRegistrationBean()方法,可以看到RegistrationBean的创建,并且设置排序属性值,最后将FilterRegistrationBean存入this.initializers
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
for (Entry<String, B> entry : entries) {
String beanName = entry.getKey();
B bean = entry.getValue();
if (this.seen.add(bean)) {
// One that we haven't already seen
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
this.initializers.add(type, registration);
if (logger.isTraceEnabled()) {
logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
+ order + ", resource=" + getResourceDescription(beanName, beanFactory));
}
}
}
}
}
分析this.initializers的操作,没有直接取值的操作,但在ServletContextInitializerBeans()构造函数中间接赋值给了this.sortedList,而查看调用栈也可以得知
而this.initializers向this.sortedList赋值时最重要操作是如下代码:
将所有AbstractFilterRegistrationBean按照AnnotationAwareOrderComparator的规则进行排序,因为此类型实现了Ordered接口所以优先按照接口排序值排序,当没有实现Ordered接口时才按照@Order注解排序,
然后转换为不可修改的集合赋值给this.sortedList
List<ServletContextInitializer> sortedInitializers=this.initializers.values().stream()
.flatMap((value)->value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList=Collections.unmodifiableList(sortedInitializers);
在ServletContextInitializerBeans类中找到this.sortedList的操作,iterator()方法添加断点
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
@Override
public Iterator<ServletContextInitializer> iterator() {
return this.sortedList.iterator();
}
}
放开上一个断点,进入iterator(),查看调用栈
文章来源:https://www.toymoban.com/news/detail-808317.html
在selfInitialize()方法内getServletContextInitializerBeans()获取到this.sortedList,即排序后的FilterRegistrationBean,按照顺序挨个执行文章来源地址https://www.toymoban.com/news/detail-808317.html
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
}
d.完
到了这里,关于SpringBoot在使用SpringSecurity时,配置跨域过滤器CorsFilter不生效分析解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!