序
本文主要聊一下spring security的permitAll以及webIgnore的区别
permitAll配置实例
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
.authorizeRequests()
.antMatchers("/css/**", "/js/**","/fonts/**").permitAll()
.anyRequest().authenticated();
}复制代码
web ignore配置实例
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/fonts/**");
}复制代码
二者区别
顾名思义,WebSecurity主要是配置跟web资源相关的,比如css、js、images等等,但是这个还不是本质的区别,关键的区别如下:
AnonymousAuthenticationFilter
spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java
* Detects if there is no { @code Authentication} object in the * { @code SecurityContextHolder}, and populates it with one if needed. * @author Ben Alex * @author Luke Taylor public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean { //...... public void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (SecurityContextHolder.getContext().getAuthentication() == null ) { SecurityContextHolder.getContext().setAuthentication( createAuthentication((HttpServletRequest) req)); if (logger.isDebugEnabled()) { logger.debug( "Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'" ); else { if (logger.isDebugEnabled()) { logger.debug( "SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'" ); chain.doFilter(req, res); protected Authentication createAuthentication (HttpServletRequest request) { AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken (key, principal, authorities); auth.setDetails(authenticationDetailsSource.buildDetails(request)); return auth; //...... } 复制代码这个filter的主要功能就是给没有登陆的用户,填充AnonymousAuthenticationToken到SecurityContextHolder的Authentication,后续依赖Authentication的代码可以统一处理。
FilterComparator
spring-security-config-4.1.4.RELEASE-sources.jar!/org/springframework/security/config/annotation/web/builders/FilterComparator.java
final class FilterComparator implements Comparator<Filter>, Serializable { private static final int STEP = 100; private Map<String, Integer> filterToOrder = new HashMap<String, Integer>(); FilterComparator() { int order = 100; put(ChannelProcessingFilter.class, order); order += STEP; put(ConcurrentSessionFilter.class, order); order += STEP; put(WebAsyncManagerIntegrationFilter.class, order); order += STEP; put(SecurityContextPersistenceFilter.class, order); order += STEP; put(HeaderWriterFilter.class, order); order += STEP; put(CorsFilter.class, order); order += STEP; put(CsrfFilter.class, order); order += STEP; put(LogoutFilter.class, order); order += STEP; put(X509AuthenticationFilter.class, order); order += STEP; put(AbstractPreAuthenticatedProcessingFilter.class, order); order += STEP; filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order); order += STEP; put(UsernamePasswordAuthenticationFilter.class, order); order += STEP; put(ConcurrentSessionFilter.class, order); order += STEP; filterToOrder.put( "org.springframework.security.openid.OpenIDAuthenticationFilter", order); order += STEP; put(DefaultLoginPageGeneratingFilter.class, order); order += STEP; put(ConcurrentSessionFilter.class, order); order += STEP; put(DigestAuthenticationFilter.class, order); order += STEP; put(BasicAuthenticationFilter.class, order); order += STEP; put(RequestCacheAwareFilter.class, order); order += STEP; put(SecurityContextHolderAwareRequestFilter.class, order); order += STEP; put(JaasApiIntegrationFilter.class, order); order += STEP; put(RememberMeAuthenticationFilter.class, order); order += STEP; put(AnonymousAuthenticationFilter.class, order); order += STEP; put(SessionManagementFilter.class, order); order += STEP; put(ExceptionTranslationFilter.class, order); order += STEP; put(FilterSecurityInterceptor.class, order); order += STEP; put(SwitchUserFilter.class, order); //...... }复制代码
这个类定义了spring security内置的filter的优先级,AnonymousAuthenticationFilter在倒数第五个执行,在FilterSecurityInterceptor这个类之前。
FilterSecurityInterceptor
spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/access/intercept/FilterSecurityInterceptor.java
* Performs security handling of HTTP resources via a filter implementation. * The <code>SecurityMetadataSource</code> required by this security interceptor is of * type {@link FilterInvocationSecurityMetadataSource}. * Refer to {@link AbstractSecurityInterceptor} for details on the workflow. * @author Ben Alex * @author Rob Winch public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { //...... } 复制代码这个相当于spring security的核心处理类了,它继承抽象类AbstractSecurityInterceptor
spring-security-core-4.2.3.RELEASE-sources.jar!/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java
public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware { //...... protected InterceptorStatusToken beforeInvocation(Object object) { Assert.notNull(object, "Object was null"); final boolean debug = logger.isDebugEnabled(); if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { throw new IllegalArgumentException( "Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + getSecureObjectClass()); Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource() .getAttributes(object); if (attributes == null || attributes.isEmpty()) { if (rejectPublicInvocations) { throw new IllegalArgumentException( "Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. " + "This indicates a configuration error because the " + "rejectPublicInvocations property is set to 'true'"); if (debug) { logger.debug("Public object - authentication not attempted"); publishEvent(new PublicInvocationEvent(object)); return null; // no further work post-invocation if (debug) { logger.debug("Secure object: " + object + "; Attributes: " + attributes); if (SecurityContextHolder.getContext().getAuthentication() == null) { credentialsNotFound(messages.getMessage( "AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes); Authentication authenticated = authenticateIfRequired(); // Attempt authorization try { this.accessDecisionManager.decide(authenticated, object, attributes); catch (AccessDeniedException accessDeniedException) { publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; if (debug) { logger.debug("Authorization successful"); if (publishAuthorizationSuccess) { publishEvent(new AuthorizedEvent(object, attributes, authenticated)); // Attempt to run as a different user Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes); if (runAs == null) { if (debug) { logger.debug("RunAsManager did not change Authentication object"); // no further work post-invocation return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object); else { if (debug) { logger.debug("Switching to RunAs Authentication: " + runAs); SecurityContext origCtx = SecurityContextHolder.getContext(); SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext()); SecurityContextHolder.getContext().setAuthentication(runAs); // need to revert to token.Authenticated post-invocation return new InterceptorStatusToken(origCtx, true, attributes, object); //...... }复制代码
主要的逻辑在这个beforeInvocation方法,它就依赖了authentication
private Authentication authenticateIfRequired() { Authentication authentication = SecurityContextHolder.getContext() .getAuthentication(); if (authentication.isAuthenticated() && !alwaysReauthenticate) { if (logger.isDebugEnabled()) { logger.debug("Previously Authenticated: " + authentication); return authentication; authentication = authenticationManager.authenticate(authentication); // We don't authenticated.setAuthentication(true), because each provider should do // that if (logger.isDebugEnabled()) { logger.debug("Successfully Authenticated: " + authentication); SecurityContextHolder.getContext().setAuthentication(authentication); return authentication; }复制代码
这个方法判断authentication如果是已经校验过的,则返回;没有校验过的话,则调用authenticationManager进行鉴权。
而AnonymousAuthenticationFilter设置的authentication在这个时候就派上用场了
spring-security-core-4.2.3.RELEASE-sources.jar!/org/springframework/security/authentication/AnonymousAuthenticationToken.java
```
public class AnonymousAuthenticationToken extends AbstractAuthenticationToken implements
Serializable {
private AnonymousAuthenticationToken(Integer keyHash, Object principal,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
if (principal == null || "".equals(principal)) { throw new IllegalArgumentException("principal cannot be null or empty"); Assert.notEmpty(authorities, "authorities cannot be null or empty"); this.keyHash = keyHash; this.principal = principal; setAuthenticated(true); //......复制代码
}
```它默认就是authenticated
web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的; 而permitAll,会给没有登录的用户适配一个AnonymousAuthenticationToken,设置到SecurityContextHolder,方便后面的filter可以统一处理authentication。 spring security 的几个细节 Spring Security – security none, filters none, access permitAll Spring Security permitAll() not allowing anonymous access Difference between access=“permitAll” and filters=“none”?