Spring实战----源码解析Spring Security4.1.3中的过滤器Filter配置
Posted Herman-Hong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring实战----源码解析Spring Security4.1.3中的过滤器Filter配置相关的知识,希望对你有一定的参考价值。
Spring Security的底层是通过一系列的Filter来管理的,每个Filter都有其自身的功能,那么这些Filter是怎么配置到系统中的?
一、web.xml配置
通常web使用会在web.xml中进行如下配置
<!-- Spring-security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
而DelegatingFilterProxy并不是真正工作的Filter,org.springframework.web.filter.DelegatingFilterProxy.java
@Override
protected void initFilterBean() throws ServletException
synchronized (this.delegateMonitor)
if (this.delegate == null)
// If no target bean name specified, use filter name.
if (this.targetBeanName == null)
this.targetBeanName = getFilterName();
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null)
this.delegate = initDelegate(wac);
会从WebApplicationContext中得到delegate,而真正的工作有delegate来实现
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null)
synchronized (this.delegateMonitor)
if (this.delegate == null)
WebApplicationContext wac = findWebApplicationContext();
if (wac == null)
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
this.delegate = initDelegate(wac);
delegateToUse = this.delegate;
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
而这个delegate是FilterChainProxy,从代码中可以看出这个FilterChainProxy是Spring在解析配置文件时装配到上下文中的,并且beanName为"springSecurityFilterChain",因此在web.xml中需要配置filter-name为springSecurityFilterChain,否则就不能获取到FilterChainProxy。最终这个代理还是把工作交给了FilterList来处理,其中保存的就是Security系统中的一系列Filter。那么这些Filter也是在解析配置文件的时候注册到上下文中的。下面具体看下Security配置文件的解析,主要是<http>标签
二、<http>标签的解析
在配置文件中需要配置<http>才能使用Security,
<http auto-config="true" use-expressions="true" >
<form-login
login-page="/login"
authentication-failure-url="/login?error"
login-processing-url="/login"
authentication-success-handler-ref="myAuthenticationSuccessHandler" />
<!-- 认证成功用自定义类myAuthenticationSuccessHandler处理 -->
<logout logout-url="/logout"
logout-success-url="/"
invalidate-session="true"
delete-cookies="JSESSIONID"/>
<!-- 禁用csrf功能 -->
<csrf disabled="true" />
<intercept-url pattern="/order/*" access="hasRole('ROLE_USER')"/>
</http>
接下来看下,解析该标签时做了什么?该标签是自定义标签,查看spring.handlers(在spring-security-config包中)
http\\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler
SecurityNamespaceHandler.java
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
由HttpSecurityBeanDefinitionParser解析器解析
/**
* The aim of this method is to build the list of filters which have been defined by
* the namespace elements and attributes within the <http> configuration, along
* with any custom-filter's linked to user-defined filter beans.
* <p>
* By the end of this method, the default <tt>FilterChainProxy</tt> bean should have
* been registered and will have the map of filter chains defined, with the
* "universal" match pattern mapped to the list of beans which have been parsed here.
*/
@SuppressWarnings( "unchecked" )
public BeanDefinition parse(Element element, ParserContext pc)
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(
element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element)); //Filter注册
// Obtain the filter chains and add the new chain to it
BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(
BeanIds.FILTER_CHAINS);
List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean
.getPropertyValues().getPropertyValue("sourceList").getValue();
filterChains.add(createFilterChain(element, pc)); //一系列Filter的注册
pc.popAndRegisterContainingComponent();
return null;
先看registerFilterChainProxyIfNecessary
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source)
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY))
return;
// Not already registered, so register the list of filter chains and the
// FilterChainProxy
BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean,
BeanIds.FILTER_CHAINS));
BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder
.rootBeanDefinition(FilterChainProxy.class);
fcpBldr.getRawBeanDefinition().setSource(source);
fcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS);
fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(
DefaultFilterChainValidator.class));
BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
pc.registerBeanComponent(new BeanComponentDefinition(fcpBean,
BeanIds.FILTER_CHAIN_PROXY));
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY,
BeanIds.SPRING_SECURITY_FILTER_CHAIN); //springSecurityFilterChain
其他的先不管,直接看最后,常量为,这里就是注册了名为springSecurityFilterChain的filterChainProxy类
private static final String PREFIX = "org.springframework.security.";
/** External alias for FilterChainProxy bean, for use in web.xml files */
public static final String SPRING_SECURITY_FILTER_CHAIN = "springSecurityFilterChain";
public static final String FILTER_CHAIN_PROXY = PREFIX + "filterChainProxy";
看下注册一系列Filter的地方createFilterChain
/**
* Creates the @code SecurityFilterChain bean from an <http> element.
*/
private BeanReference createFilterChain(Element element, ParserContext pc)
boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED));
if (!secured)
if (!StringUtils.hasText(element.getAttribute(ATT_PATH_PATTERN))
&& !StringUtils.hasText(ATT_REQUEST_MATCHER_REF))
pc.getReaderContext().error(
"The '" + ATT_SECURED
+ "' attribute must be used in combination with"
+ " the '" + ATT_PATH_PATTERN + "' or '"
+ ATT_REQUEST_MATCHER_REF + "' attributes.",
pc.extractSource(element));
for (int n = 0; n < element.getChildNodes().getLength(); n++)
if (element.getChildNodes().item(n) instanceof Element)
pc.getReaderContext().error(
"If you are using <http> to define an unsecured pattern, "
+ "it cannot contain child elements.",
pc.extractSource(element));
return createSecurityFilterChainBean(element, pc, Collections.emptyList());
final BeanReference portMapper = createPortMapper(element, pc);
final BeanReference portResolver = createPortResolver(portMapper, pc);
ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
BeanReference authenticationManager = createAuthenticationManager(element, pc,
authenticationProviders);
boolean forceAutoConfig = isDefaultHttpConfig(element);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element,
forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element,
forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(),
httpBldr.getRequestCache(), authenticationManager,
httpBldr.getSessionStrategy(), portMapper, portResolver,
httpBldr.getCsrfLogoutHandler());
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
authenticationProviders.addAll(authBldr.getProviders());
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
unorderedFilterChain.addAll(httpBldr.getFilters()); //this
unorderedFilterChain.addAll(authBldr.getFilters());
unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
Collections.sort(unorderedFilterChain, new OrderComparator());
checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
// The list of filter beans
List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();
for (OrderDecorator od : unorderedFilterChain)
filterChain.add(od.bean);
return createSecurityFilterChainBean(element, pc, filterChain);
HttpConfigurationBuilder配置中会创建
public HttpConfigurationBuilder(Element element, boolean addAllAuth,
ParserContext pc, BeanReference portMapper, BeanReference portResolver,
BeanReference authenticationManager)
this.httpElt = element;
this.addAllAuth = addAllAuth;
this.pc = pc;
this.portMapper = portMapper;
this.portResolver = portResolver;
this.matcherType = MatcherType.fromElement(element);
interceptUrls = DomUtils.getChildElementsByTagName(element,
Elements.INTERCEPT_URL);
for (Element urlElt : interceptUrls)
if (StringUtils.hasText(urlElt.getAttribute(ATT_FILTERS)))
pc.getReaderContext()
.error("The use of \\"filters='none'\\" is no longer supported. Please define a"
+ " separate <http> element for the pattern you want to exclude and use the attribute"
+ " \\"security='none'\\".", pc.extractSource(urlElt));
String createSession = element.getAttribute(ATT_CREATE_SESSION);
if (StringUtils.hasText(createSession))
sessionPolicy = createPolicy(createSession);
else
sessionPolicy = SessionCreationPolicy.IF_REQUIRED;
createCsrfFilter(); //this
createSecurityContextPersistenceFilter();
createSessionManagementFilters();
createWebAsyncManagerFilter();
createRequestCacheFilter();
createServletApiFilter(authenticationManager);
createJaasApiFilter();
createChannelProcessingFilter();
createFilterSecurityInterceptor(authenticationManager);
createAddHeadersFilter();
createCorsFilter();
AuthenticationConfigBuilder也会创建
public AuthenticationConfigBuilder(Element element, boolean forceAutoConfig,
ParserContext pc, SessionCreationPolicy sessionPolicy,
BeanReference requestCache, BeanReference authenticationManager,
BeanReference sessionStrategy, BeanReference portMapper,
BeanReference portResolver, BeanMetadataElement csrfLogoutHandler)
this.httpElt = element;
this.pc = pc;
this.requestCache = requestCache;
autoConfig = forceAutoConfig
| "true".equals(element.getAttribute(ATT_AUTO_CONFIG));
this.allowSessionCreation = sessionPolicy != SessionCreationPolicy.NEVER
&& sessionPolicy != SessionCreationPolicy.STATELESS;
this.portMapper = portMapper;
this.portResolver = portResolver;
this.csrfLogoutHandler = csrfLogoutHandler;
createAnonymousFilter();
createRememberMeFilter(authenticationManager);
createBasicFilter(authenticationManager);
createFormLoginFilter(sessionStrategy, authenticationManager);
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
createX509Filter(authenticationManager);
createJeeFilter(authenticationManager);
createLogoutFilter();
createLoginPageFilterIfNeeded();
createUserDetailsServiceFactory();
createExceptionTranslationFilter();
单看一个createFormLoginFilter,就是针对标签<form-login>的配置解析,其实创建的是UsernamePasswordAuthenticationFilter
static final String AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter";
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager)
Element formLoginElt = DomUtils.getChildElementByTagName(httpElt,
Elements.FORM_LOGIN);
RootBeanDefinition formFilter = null;
if (formLoginElt != null || autoConfig)
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser(
"/login", "POST", AUTHENTICATION_PROCESSING_FILTER_CLASS,
requestCache, sessionStrategy, allowSessionCreation, portMapper,
portResolver);
parser.parse(formLoginElt, pc);
formFilter = parser.getFilterBean();
formEntryPoint = parser.getEntryPointBean();
loginProcessingUrl = parser.getLoginProcessingUrl();
formLoginPage = parser.getLoginPage();
if (formFilter != null)
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation",
allowSessionCreation);
formFilter.getPropertyValues().addPropertyValue("authenticationManager",
authManager);
// Id is required by login page filter
formFilterId = pc.getReaderContext().generateBeanName(formFilter);
pc.registerBeanComponent(new BeanComponentDefinition(formFilter, formFilterId));
injectRememberMeServicesRef(formFilter, rememberMeServicesId);
又交给解析器FormLoginBeanDefinitionParser,其parse方法也很长,单说上一篇中配置的authentication-success-handler-ref="myAuthenticationSuccessHandler"的作用。
public BeanDefinition parse(Element elt, ParserContext pc)
......
String successHandlerRef = null;
.......//private static final String ATT_SUCCESS_HANDLER_REF = "authentication-success-handler-ref";
successHandlerRef = elt.getAttribute(ATT_SUCCESS_HANDLER_REF);
filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault,
loginPage, authenticationFailureUrl, successHandlerRef,
failureHandlerRef, authDetailsSourceRef, authenticationFailureForwardUrl, authenticationSuccessForwardUrl);
.......
在createFilterBean中单看successHandlerRef的处理,如果配置了则使用配置的successHandler,优先使用authentication-success-handler-ref属性配置的,其次使用authentication-success-forward-url属性配置的,否则使用默认的SavedRequestAwareAuthenticationSuccessHandler
if (StringUtils.hasText(successHandlerRef))
filterBuilder.addPropertyReference("authenticationSuccessHandler",
successHandlerRef);
else if(StringUtils.hasText(authenticationSuccessForwardUrl))
BeanDefinitionBuilder forwardSuccessHandler = BeanDefinitionBuilder
.rootBeanDefinition(ForwardAuthenticationSuccessHandler.class);
forwardSuccessHandler.addConstructorArgValue(authenticationSuccessForwardUrl);
filterBuilder.addPropertyValue("authenticationSuccessHandler", forwardSuccessHandler.getBeanDefinition());
else
BeanDefinitionBuilder successHandler = BeanDefinitionBuilder
.rootBeanDefinition(SavedRequestAwareAuthenticationSuccessHandler.class);
if ("true".equals(alwaysUseDefault))
successHandler
.addPropertyValue("alwaysUseDefaultTargetUrl", Boolean.TRUE);
successHandler.addPropertyValue("requestCache", requestCache);
successHandler.addPropertyValue("defaultTargetUrl", StringUtils
.hasText(defaultTargetUrl) ? defaultTargetUrl
: DEF_FORM_LOGIN_TARGET_URL);
filterBuilder.addPropertyValue("authenticationSuccessHandler",
successHandler.getBeanDefinition());
这里就说明了4.1.3后会将待认证的请求信息保存,认证完成后再恢复。
并且这些Filter会根据枚举类SecurityFilters进行排序
enum SecurityFilters
FIRST(Integer.MIN_VALUE), CHANNEL_FILTER, SECURITY_CONTEXT_FILTER, CONCURRENT_SESSION_FILTER,
/** @link WebAsyncManagerIntegrationFilter */
WEB_ASYNC_MANAGER_FILTER, HEADERS_FILTER, CORS_FILTER, CSRF_FILTER, LOGOUT_FILTER, X509_FILTER, PRE_AUTH_FILTER, CAS_FILTER, FORM_LOGIN_FILTER, OPENID_FILTER, LOGIN_PAGE_FILTER, DIGEST_AUTH_FILTER, BASIC_AUTH_FILTER, REQUEST_CACHE_FILTER, SERVLET_API_SUPPORT_FILTER, JAAS_API_SUPPORT_FILTER, REMEMBER_ME_FILTER, ANONYMOUS_FILTER, SESSION_MANAGEMENT_FILTER, EXCEPTION_TRANSLATION_FILTER, FILTER_SECURITY_INTERCEPTOR, SWITCH_USER_FILTER, LAST(
Integer.MAX_VALUE);
private static final int INTERVAL = 100;
private final int order;
private SecurityFilters()
order = ordinal() * INTERVAL;
private SecurityFilters(int order)
this.order = order;
public int getOrder()
return order;
根据上述的枚举类就可看出各个Filter的顺序。
三、Filter的排序方法
采用的是实现java.util.Comparator接口,利用现有api Collections.sort实现排序
Collections.sort(unorderedFilterChain, new OrderComparator());
public class OrderComparator implements Comparator<Object>
/**
* Shared default instance of @code OrderComparator.
*/
public static final OrderComparator INSTANCE = new OrderComparator();
/**
* Build an adapted order comparator with the given source provider.
* @param sourceProvider the order source provider to use
* @return the adapted comparator
* @since 4.1
*/
public Comparator<Object> withSourceProvider(final OrderSourceProvider sourceProvider)
return new Comparator<Object>()
@Override
public int compare(Object o1, Object o2)
return doCompare(o1, o2, sourceProvider);
;
@Override
public int compare(Object o1, Object o2)
return doCompare(o1, o2, null);
private int doCompare(Object o1, Object o2, OrderSourceProvider sourceProvider)
boolean p1 = (o1 instanceof PriorityOrdered);
boolean p2 = (o2 instanceof PriorityOrdered);
if (p1 && !p2)
return -1;
else if (p2 && !p1)
return 1;
// Direct evaluation instead of Integer.compareTo to avoid unnecessary object creation.
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
/**
* Determine the order value for the given object.
* <p>The default implementation checks against the given @link OrderSourceProvider
* using @link #findOrder and falls back to a regular @link #getOrder(Object) call.
* @param obj the object to check
* @return the order value, or @code Ordered.LOWEST_PRECEDENCE as fallback
*/
private int getOrder(Object obj, OrderSourceProvider sourceProvider)
Integer order = null;
if (sourceProvider != null)
Object orderSource = sourceProvider.getOrderSource(obj);
if (orderSource != null && orderSource.getClass().isArray())
Object[] sources = ObjectUtils.toObjectArray(orderSource);
for (Object source : sources)
order = findOrder(source);
if (order != null)
break;
else
order = findOrder(orderSource);
return (order != null ? order : getOrder(obj));
/**
* Determine the order value for the given object.
* <p>The default implementation checks against the @link Ordered interface
* through delegating to @link #findOrder. Can be overridden in subclasses.
* @param obj the object to check
* @return the order value, or @code Ordered.LOWEST_PRECEDENCE as fallback
*/
protected int getOrder(Object obj)
Integer order = findOrder(obj);
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
/**
* Find an order value indicated by the given object.
* <p>The default implementation checks against the @link Ordered interface.
* Can be overridden in subclasses.
* @param obj the object to check
* @return the order value, or @code null if none found
*/
protected Integer findOrder(Object obj)
return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
/**
* Determine a priority value for the given object, if any.
* <p>The default implementation always returns @code null.
* Subclasses may override this to give specific kinds of values a
* 'priority' characteristic, in addition to their 'order' semantics.
* A priority indicates that it may be used for selecting one object over
* another, in addition to serving for ordering purposes in a list/array.
* @param obj the object to check
* @return the priority value, or @code null if none
* @since 4.1
*/
public Integer getPriority(Object obj)
return null;
/**
* Sort the given List with a default OrderComparator.
* <p>Optimized to skip sorting for lists with size 0 or 1,
* in order to avoid unnecessary array extraction.
* @param list the List to sort
* @see java.util.Collections#sort(java.util.List, java.util.Comparator)
*/
public static void sort(List<?> list)
if (list.size() > 1)
Collections.sort(list, INSTANCE);
/**
* Sort the given array with a default OrderComparator.
* <p>Optimized to skip sorting for lists with size 0 or 1,
* in order to avoid unnecessary array extraction.
* @param array the array to sort
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
*/
public static void sort(Object[] array)
if (array.length > 1)
Arrays.sort(array, INSTANCE);
/**
* Sort the given array or List with a default OrderComparator,
* if necessary. Simply skips sorting when given any other value.
* <p>Optimized to skip sorting for lists with size 0 or 1,
* in order to avoid unnecessary array extraction.
* @param value the array or List to sort
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
*/
public static void sortIfNecessary(Object value)
if (value instanceof Object[])
sort((Object[]) value);
else if (value instanceof List)
sort((List<?>) value);
/**
* Strategy interface to provide an order source for a given object.
* @since 4.1
*/
public interface OrderSourceProvider
/**
* Return an order source for the specified object, i.e. an object that
* should be checked for an order value as a replacement to the given object.
* <p>Can also be an array of order source objects.
* <p>If the returned object does not indicate any order, the comparator
* will fall back to checking the original object.
* @param obj the object to find an order source for
* @return the order source for that object, or @code null if none found
*/
Object getOrderSource(Object obj);
sort实现
/**
* Sorts the specified list according to the order induced by the
* specified comparator. All elements in the list must be <i>mutually
* comparable</i> using the specified comparator (that is,
* @code c.compare(e1, e2) must not throw a @code ClassCastException
* for any elements @code e1 and @code e2 in the list).
*
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* <p>The specified list must be modifiable, but need not be resizable.
*
* @implNote
* This implementation defers to the @link List#sort(Comparator)
* method using the specified list and comparator.
*
* @param <T> the class of the objects in the list
* @param list the list to be sorted.
* @param c the comparator to determine the order of the list. A
* @code null value indicates that the elements' <i>natural
* ordering</i> should be used.
* @throws ClassCastException if the list contains elements that are not
* <i>mutually comparable</i> using the specified comparator.
* @throws UnsupportedOperationException if the specified list's
* list-iterator does not support the @code set operation.
* @throws IllegalArgumentException (optional) if the comparator is
* found to violate the @link Comparator contract
* @see List#sort(Comparator)
*/
@SuppressWarnings("unchecked", "rawtypes")
public static <T> void sort(List<T> list, Comparator<? super T> c)
list.sort(c);
还有一种方法实现序列比较,就是参与比较类实现Comparable接口,利用Collections如下sort方法
/**
* Sorts the specified list into ascending order, according to the
* @linkplain Comparable natural ordering of its elements.
* All elements in the list must implement the @link Comparable
* interface. Furthermore, all elements in the list must be
* <i>mutually comparable</i> (that is, @code e1.compareTo(e2)
* must not throw a @code ClassCastException for any elements
* @code e1 and @code e2 in the list).
*
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* <p>The specified list must be modifiable, but need not be resizable.
*
* @implNote
* This implementation defers to the @link List#sort(Comparator)
* method using the specified list and a @code null comparator.
*
* @param <T> the class of the objects in the list
* @param list the list to be sorted.
* @throws ClassCastException if the list contains elements that are not
* <i>mutually comparable</i> (for example, strings and integers).
* @throws UnsupportedOperationException if the specified list's
* list-iterator does not support the @code set operation.
* @throws IllegalArgumentException (optional) if the implementation
* detects that the natural ordering of the list elements is
* found to violate the @link Comparable contract
* @see List#sort(Comparator)
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void sort(List<T> list)
list.sort(null);
当然数组类Arrays也有类似的方法。
总结:
security是基于过滤器的,过滤器的配置都是在配置文件解析时做的。
以上是关于Spring实战----源码解析Spring Security4.1.3中的过滤器Filter配置的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot实战源码解析Spring Boot自动配置原理
Spring Boot实战源码解析Spring Boot自动配置原理
Spring Boot实战源码解析Spring Boot自动配置原理