聊聊spring security的permitAll以及webIgnore


声明:本文转载自https://my.oschina.net/go4it/blog/1579826,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

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

permitAll配置实例

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {     @Override     public void configure(HttpSecurity http) throws Exception {         http                 .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等等,但是这个还不是本质的区别,关键的区别如下:

  • 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

本文发表于2017年11月26日 10:33
(c)注:本文转载自https://my.oschina.net/go4it/blog/1579826,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2921 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1