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是基于过滤器的,过滤器的配置都是在配置文件解析时做的。