聊聊spring security的permitAll以及webIgnore

前端 0

本文主要聊一下spring security的permitAll以及webIgnore的区别

permitAll配置实例

@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Override    public void configure(HttpSecurity http) throws Exception {        http                .authorizeRequests()                .antMatchers("/css/**", "/js/**","/fonts/**").permitAll()                .anyRequest().authenticated();    }}

web ignore配置实例

@EnableWebSecuritypublic 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等等,但是这个还不是本质的区别,关键的区别如下:

  • ingore是完全绕过了spring security的所有filter,相当于不走spring security
  • permitall没有绕过spring security,其中包含了登录的以及匿名的。

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. * <p> * The <code>SecurityMetadataSource</code> required by this security interceptor is of * type {@link FilterInvocationSecurityMetadataSource}. * <p> * Refer to {@link AbstractSecurityInterceptor} for details on the workflow. * </p> * * @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。

doc

  • spring security 的几个细节
  • Spring Security – security none, filters none, access permitAll
  • Spring Security permitAll() not allowing anonymous access
  • Difference between access=“permitAll” and filters=“none”?

作者:go4it

喜欢 0

也许您对下面的内容还感兴趣: