Spring Security源码解析(一)——认证和鉴权

目录

认证过程

AuthenticationManager

Authentication

AbstractAuthenticationToken

UsernamePasswordAuthenticationToken

 RememberMeAuthenticationToken

 AbstractOAuth2TokenAuthenticationToken

ProviderManager

AuthenticationProvider

DaoAuthenticationProvider

AbstractUserDetailsAuthenticationProvider 

UserDetails

UserDetailsChecker 

DaoAuthenticationProvider属性

DaoAuthenticationProvider.retrieveUser()

 createSuccessAuthentication()

additionalAuthenticationChecks()

UserDetailsService

 JdbcUserDetailsManager

InMemoryUserDetailsManager

总结

引入

AuthenticationManagerBuilder

performBuild()

Filters

UsernamePasswordAuthenticationFilter

AbstractAuthenticationProcessingFilter

attemptAuthentication

AnonymousAuthenticationFilter

ExceptionTranslationFilter

FilterSecurityInterceptor

AbstractSecurityInterceptor

AccessDecisionManager 

 AbstractAccessDecisionManager 

AccessDecisionVoter

 WebExpressionVoter

RoleVoter 

AuthenticatedVoter

AffirmativeBased

ConsensusBased

UnanimousBased

密码验证时序图


Spring Security解决用户认证(Authentication)和用户授权(Authorization)2个问题。

源码地址:https://github.com/spring-projects/spring-security/tree/5.2.1.RELEASE

认证过程

AuthenticationManager

该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数;

public interface AuthenticationManager {
	Authentication authenticate(Authentication authentication)			throws AuthenticationException;
}

Authentication

Authentication封装了验证请求信息。

public interface Authentication extends Principal, Serializable {
	//获取 授权信息
	Collection<? extends GrantedAuthority> getAuthorities();

	//凭据
	Object getCredentials();

	//用户信息
	Object getDetails();

	//主体,可以理解验证时的用户名。
	Object getPrincipal();

	//是否被认证。
	boolean isAuthenticated();

	//认证结果设置。
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

 授权信息:

public interface GrantedAuthority extends Serializable {
	String getAuthority();
}

 根据不同的认证类型,定义了多种实现:

AbstractAuthenticationToken

	private final Collection<GrantedAuthority> authorities;
	private Object details;
	private boolean authenticated = false;

UsernamePasswordAuthenticationToken

	private final Object principal;
	private Object credentials;

 RememberMeAuthenticationToken

	private final Object principal;
	private final int keyHash;

 AbstractOAuth2TokenAuthenticationToken

	private Object principal;
	private Object credentials;
	private T token;

ProviderManager

它是 AuthenticationManager 的一个实现类,提供了基本的认证逻辑和方法;它包含了一个 List<AuthenticationProvider> 对象,通过 AuthenticationProvider 接口来扩展出不同的认证提供者(当Spring Security默认提供的实现类不能满足需求的时候可以扩展AuthenticationProvider 覆盖supports(Class<?> authentication)方法);

	private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
//验证者 列表
	private List<AuthenticationProvider> providers = Collections.emptyList();
	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
	private AuthenticationManager parent;
	private boolean eraseCredentialsAfterAuthentication = true;

AuthenticationManager 通过 authenticate(Authentication) 方法对其进行验证;AuthenticationProvider实现类用来支撑对 Authentication 对象的验证动作;UsernamePasswordAuthenticationToken实现了 Authentication主要是将用户输入的用户名和密码进行封装,并供给 AuthenticationManager 进行验证;验证完成以后将返回一个认证成功的 Authentication 对象;

public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		AuthenticationException parentException = null;
		Authentication result = null;
		Authentication parentResult = null;
		boolean debug = logger.isDebugEnabled();

        //循环每个AuthenticationProvider 
		for (AuthenticationProvider provider : getProviders()) {
            //如果不支持Authentication,则继续下一个
			if (!provider.supports(toTest)) {
				continue;
			}

			if (debug) {
				logger.debug("Authentication attempt using "
						+ provider.getClass().getName());
			}

			try {
                //验证
				result = provider.authenticate(authentication);
                //如果验证不为null,则表示验证成功。
				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException | InternalAuthenticationServiceException e) {
				prepareException(e, authentication);
				// SEC-546: Avoid polling additional providers if auth failure is due to
				// invalid account status
				throw e;
			} catch (AuthenticationException e) {
				lastException = e;
			}
		}
        //如果所有provider都不支持验证,则使用parent验证。
		if (result == null && parent != null) {
			// Allow the parent to try.
			try {
				result = parentResult = parent.authenticate(authentication);
			}
			catch (ProviderNotFoundException e) {
				// ignore as we will throw below if no other exception occurred prior to
				// calling parent and the parent
				// may throw ProviderNotFound even though a provider in the child already
				// handled the request
			}
			catch (AuthenticationException e) {
				lastException = parentException = e;
			}
		}
        //擦除敏感信息。
		if (result != null) {
			if (eraseCredentialsAfterAuthentication
					&& (result instanceof CredentialsContainer)) {
				((CredentialsContainer) result).eraseCredentials();
			}

			// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
			// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
			if (parentResult == null) {
				eventPublisher.publishAuthenticationSuccess(result);
			}
			return result;
		}

		// Parent was null, or didn't authenticate (or throw an exception).

		if (lastException == null) {
			lastException = new ProviderNotFoundException(messages.getMessage(
					"ProviderManager.providerNotFound",
					new Object[] { toTest.getName() },
					"No AuthenticationProvider found for {0}"));
		}

		// If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent
		// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
		if (parentException == null) {
			prepareException(lastException, authentication);
		}

		throw lastException;
	}

AuthenticationProvider

AuthenticationProvider提供用户认证。

public interface AuthenticationProvider {
	Authentication authenticate(Authentication authentication)			throws AuthenticationException;
	boolean supports(Class<?> authentication);
}

 Spring Security提供了以下认证方式:

  • 用户名,密码
  • LDAP
  • 匿名方式
  • RememberMe
  • JWT
  • OAuth2
  • ... ...

DaoAuthenticationProvider

DaoAuthenticationProvider支持Authentication为UsernamePasswordAuthenticationToken的认证。

	//AbstractUserDetailsAuthenticationProvider方法
    public boolean supports(Class<?> authentication) {
		return (UsernamePasswordAuthenticationToken.class
				.isAssignableFrom(authentication));
	}

AbstractUserDetailsAuthenticationProvider 

AbstractUserDetailsAuthenticationProviderDaoAuthenticationProvider提供了基本的认证方法。

public abstract class AbstractUserDetailsAuthenticationProvider implements
		AuthenticationProvider, InitializingBean, MessageSourceAware {

	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
	private UserCache userCache = new NullUserCache();
	private boolean forcePrincipalAsString = false;
	protected boolean hideUserNotFoundExceptions = true;
	private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();
	private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();
	private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
}

 认证过程如下:


	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
//T0:
		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
				() -> messages.getMessage(
						"AbstractUserDetailsAuthenticationProvider.onlySupports",
						"Only UsernamePasswordAuthenticationToken is supported"));

//T1:
		String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
				: authentication.getName();
//T2:
		boolean cacheWasUsed = true;
		UserDetails user = this.userCache.getUserFromCache(username);
//T3:
		if (user == null) {
			cacheWasUsed = false;

			try {
//T4:
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);
			}
			catch (UsernameNotFoundException notFound) {
				logger.debug("User '" + username + "' not found");

				if (hideUserNotFoundExceptions) {
					throw new BadCredentialsException(messages.getMessage(
							"AbstractUserDetailsAuthenticationProvider.badCredentials",
							"Bad credentials"));
				}
				else {
					throw notFound;
				}
			}

			Assert.notNull(user,
					"retrieveUser returned null - a violation of the interface contract");
		}

		try {
//T5:
			preAuthenticationChecks.check(user);
//T6:
			additionalAuthenticationChecks(user,
					(UsernamePasswordAuthenticationToken) authentication);
		}
		catch (AuthenticationException exception) {
			if (cacheWasUsed) {
				// There was a problem, so try again after checking
				// we're using latest data (i.e. not from the cache)
				cacheWasUsed = false;
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);
				preAuthenticationChecks.check(user);
				additionalAuthenticationChecks(user,
						(UsernamePasswordAuthenticationToken) authentication);
			}
			else {
				throw exception;
			}
		}
//T7:
		postAuthenticationChecks.check(user);

		if (!cacheWasUsed) {
			this.userCache.putUserInCache(user);
		}

		Object principalToReturn = user;

		if (forcePrincipalAsString) {
			principalToReturn = user.getUsername();
		}
//T8:
		return createSuccessAuthentication(principalToReturn, authentication, user);
	}

T0:判断是否是UsernamePasswordAuthenticationToken。

T1:获取userName,默认:NONE_PROVIDED

T2:从缓存中获取用户信息。

T3:如果缓存中没有用户信息,则通过retrieveUser获取(由子类实现)。

T4:通过retrieveUser获取用户信息。

T5:验证前check。

T6:附加check(由子类实现)。

T7:验证

T8:验证成功,创建Authentication。重新 new 了一个 UsernamePasswordAuthenticationToken,因为到这里认证已经通过了,所以将 authorities 注入进去,并设置 authenticated 为 true,即不再需要认证。

	protected abstract UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException;

	protected abstract void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException;

UserDetails

UserDetails封装认证用户的详细信息。

public interface UserDetails extends Serializable {
	//权限
	Collection<? extends GrantedAuthority> getAuthorities();
	String getPassword();
	String getUsername();
//是否账户过期
	boolean isAccountNonExpired();
//是否locked
	boolean isAccountNonLocked();
//凭据过期
	boolean isCredentialsNonExpired();
//账户是否可用
	boolean isEnabled();
}

UserDetailsChecker 

验证用户。

  • AccountStatusUserDetailsChecker

验证用户状态。是否可用,过期,锁定,凭据是否过期

  • DefaultPostAuthenticationChecks

验证用户状态。凭据是否过期。

  • DefaultPreAuthenticationChecks

验证用户状态。是否可用,过期,锁定

DaoAuthenticationProvider属性

private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";

	private PasswordEncoder passwordEncoder;

	private volatile String userNotFoundEncodedPassword;

	private UserDetailsService userDetailsService;

	private UserDetailsPasswordService userDetailsPasswordService;

DaoAuthenticationProvider.retrieveUser()

protected final UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
		try {
//T1:UserDetailsService加载用户信息
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
			if (loadedUser == null) {
				throw new InternalAuthenticationServiceException(
						"UserDetailsService returned null, which is an interface contract violation");
			}
			return loadedUser;
		}
		catch (UsernameNotFoundException ex) {
			mitigateAgainstTimingAttack(authentication);
			throw ex;
		}
		catch (InternalAuthenticationServiceException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
		}
	}

 createSuccessAuthentication()

验证后对密码进行加密。

	@Override
	protected Authentication createSuccessAuthentication(Object principal,
			Authentication authentication, UserDetails user) {
		boolean upgradeEncoding = this.userDetailsPasswordService != null
				&& this.passwordEncoder.upgradeEncoding(user.getPassword());
		if (upgradeEncoding) {
			String presentedPassword = authentication.getCredentials().toString();
			String newPassword = this.passwordEncoder.encode(presentedPassword);
			user = this.userDetailsPasswordService.updatePassword(user, newPassword);
		}
		return super.createSuccessAuthentication(principal, authentication, user);
	}

additionalAuthenticationChecks()

通过用户名,密码进行验证。

@SuppressWarnings("deprecation")
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		if (authentication.getCredentials() == null) {
			logger.debug("Authentication failed: no credentials provided");

			throw new BadCredentialsException(messages.getMessage(
					"AbstractUserDetailsAuthenticationProvider.badCredentials",
					"Bad credentials"));
		}

		String presentedPassword = authentication.getCredentials().toString();

		if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
			logger.debug("Authentication failed: password does not match stored value");

			throw new BadCredentialsException(messages.getMessage(
					"AbstractUserDetailsAuthenticationProvider.badCredentials",
					"Bad credentials"));
		}
	}

UserDetailsService

通过用户名获取用户信息。

public interface UserDetailsService {
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

 JdbcUserDetailsManager

基于JDBC访问UserDetail。默认需要包括以下表结构:

users (username, password, enabled)
authorities (username, authority) 
group_authorities (group_id, authority)
groups (id,group_name)
group_members (group_id, username)

InMemoryUserDetailsManager

基于内存访问UserDetails

总结

UserDetailsService接口作为桥梁,是DaoAuthenticationProvier与特定用户信息来源进行解耦的地方,UserDetailsServiceUserDetailsUserDetailsManager所构成;UserDetailsUserDetailsManager各司其责,一个是对基本用户信息进行封装,一个是对基本用户信息进行管理;

特别注意UserDetailsServiceUserDetails以及UserDetailsManager都是可被用户自定义的扩展点,我们可以继承这些接口提供自己的读取用户来源和管理用户的方法,比如我们可以自己实现一个 与特定 ORM 框架,比如 Mybatis 或者 Hibernate,相关的UserDetailsServiceUserDetailsManager

引入

@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class,
		OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
	boolean debug() default false;
}
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
public class AuthenticationConfiguration {

	private ApplicationContext applicationContext;
	private AuthenticationManager authenticationManager;
	private boolean authenticationManagerInitialized;
	private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections
			.emptyList();
	private ObjectPostProcessor<Object> objectPostProcessor;
}

 AuthenticationManagerBuilder的实现类DefaultPasswordEncoderAuthenticationManagerBuilder 

	@Bean
	public AuthenticationManagerBuilder authenticationManagerBuilder(
			ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
		LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
		AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);

		DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
		if (authenticationEventPublisher != null) {
			result.authenticationEventPublisher(authenticationEventPublisher);
		}
		return result;
	}

AuthenticationManagerBuilder

	private AuthenticationManager parentAuthenticationManager;
	private List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
	private UserDetailsService defaultUserDetailsService;
	private Boolean eraseCredentials;
	private AuthenticationEventPublisher eventPublisher;

performBuild()

构造ProviderManager

	protected ProviderManager performBuild() throws Exception {
		if (!isConfigured()) {
			logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
			return null;
		}
		ProviderManager providerManager = new ProviderManager(authenticationProviders,
				parentAuthenticationManager);
		if (eraseCredentials != null) {
			providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
		}
		if (eventPublisher != null) {
			providerManager.setAuthenticationEventPublisher(eventPublisher);
		}
		providerManager = postProcess(providerManager);
		return providerManager;
	}

Filters

登录过程中会提供很多Filter用于处理登录请求。认证相关的主要包含后面描述的几个

UsernamePasswordAuthenticationFilter

基于用户名和密码的认证Filter。

AbstractAuthenticationProcessingFilter

属性

protected ApplicationEventPublisher eventPublisher;
	protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
//
	private AuthenticationManager authenticationManager;
	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
//RememberMeServices 
	private RememberMeServices rememberMeServices = new NullRememberMeServices();
//请求匹配器
	private RequestMatcher requiresAuthenticationRequestMatcher;

	private boolean continueChainBeforeSuccessfulAuthentication = false;

	private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();

	private boolean allowSessionCreation = true;
//认证成功handler
	private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
//认证失败handler
	private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

doFilter()

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
//如果请求不匹配Filter,则使用原始chain。
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);

			return;
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Request is to process authentication");
		}

		Authentication authResult;

		try {
//尝试认证
			authResult = attemptAuthentication(request, response);
//结果为null,表示认证失败。
			if (authResult == null) {
				// return immediately as subclass has indicated that it hasn't completed
				// authentication
				return;
			}
//
			sessionStrategy.onAuthentication(authResult, request, response);
		}
		catch (InternalAuthenticationServiceException failed) {
			logger.error(
					"An internal error occurred while trying to authenticate the user.",
					failed);
			unsuccessfulAuthentication(request, response, failed);

			return;
		}
		catch (AuthenticationException failed) {
			// Authentication failed
			unsuccessfulAuthentication(request, response, failed);

			return;
		}

		// 如果允许验证继续其他认真。
		if (continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		}
        //认证成功,回调
		successfulAuthentication(request, response, chain, authResult);
	}

 真实验证由子类实现。

	public abstract Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException, IOException,
			ServletException;

验证成功或失败回调

protected void successfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain, Authentication authResult)
			throws IOException, ServletException {

		if (logger.isDebugEnabled()) {
			logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
					+ authResult);
		}
//设置验证结果
		SecurityContextHolder.getContext().setAuthentication(authResult);
//记住我
		rememberMeServices.loginSuccess(request, response, authResult);

		// Fire event
		if (this.eventPublisher != null) {
			eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
					authResult, this.getClass()));
		}
//successHandler回调
		successHandler.onAuthenticationSuccess(request, response, authResult);
	}



protected void unsuccessfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException failed)
			throws IOException, ServletException {
		SecurityContextHolder.clearContext();

		if (logger.isDebugEnabled()) {
			logger.debug("Authentication request failed: " + failed.toString(), failed);
			logger.debug("Updated SecurityContextHolder to contain null Authentication");
			logger.debug("Delegating to authentication failure handler " + failureHandler);
		}

		rememberMeServices.loginFail(request, response);

		failureHandler.onAuthenticationFailure(request, response, failed);
	}

attemptAuthentication

public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
//必须是POST方法
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}
//获取用户名和密码
		String username = obtainUsername(request);
		String password = obtainPassword(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim();
//封装请求
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
//调用AuthenticationManager()验证
		return this.getAuthenticationManager().authenticate(authRequest);
	}

AnonymousAuthenticationFilter

AnonymousAuthenticationFilter过滤器是在UsernamePasswordAuthenticationFilter等过滤器之后,如果它前面的过滤器都没有认证成功,Spring Security则为当前的SecurityContextHolder中添加一个Authenticaiton 的匿名实现类AnonymousAuthenticationToken;

	public AnonymousAuthenticationFilter(String key) {
		this(key, "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
	}

doFilter方法,直接触发原始chain过滤。

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {

		if (SecurityContextHolder.getContext().getAuthentication() == null) {
			SecurityContextHolder.getContext().setAuthentication(
					createAuthentication((HttpServletRequest) req));
... ... 
		}
		else {
			... ... 
			}
		}

		chain.doFilter(req, res);
	}

//设置匿名token
protected Authentication createAuthentication(HttpServletRequest request) {
		AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
				principal, authorities);
		auth.setDetails(authenticationDetailsSource.buildDetails(request));

		return auth;
	}

ExceptionTranslationFilter

ExceptionTranslationFilter 异常处理过滤器,该过滤器用来处理在系统认证授权过程中抛出的异常(也就是下一个过滤器FilterSecurityInterceptor),主要是 处理 AuthenticationException 和 AccessDeniedException 

FilterSecurityInterceptor

此过滤器为认证授权过滤器链中最后一个过滤器,该过滤器之后就是请求真正的处理逻辑。

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		FilterInvocation fi = new FilterInvocation(request, response, chain);
		invoke(fi);
	}


public void invoke(FilterInvocation fi) throws IOException, ServletException {
		if ((fi.getRequest() != null)
				&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
				&& observeOncePerRequest) {
            //已经处理。
            //直接调用真实服务。
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		}
		else {
			// 第一次调用,处理checking。
			if (fi.getRequest() != null && observeOncePerRequest) {
            //设置为true。
				fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
			}
            //前置处理
			InterceptorStatusToken token = super.beforeInvocation(fi);

			try {
            //真实业务逻辑。
				fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
			}
			finally {
				super.finallyInvocation(token);
			}
            //后置处理。
			super.afterInvocation(token, null);
		}
	}

AbstractSecurityInterceptor

	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
	private ApplicationEventPublisher eventPublisher;
	private AccessDecisionManager accessDecisionManager;
	private AfterInvocationManager afterInvocationManager;
	private AuthenticationManager authenticationManager = new NoOpAuthenticationManager();
	private RunAsManager runAsManager = new NullRunAsManager();

beforeInvocation

//object:请求的url。
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);
//如果没有,则返回null。
		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);
		}
	}
	protected void finallyInvocation(InterceptorStatusToken token) {
		if (token != null && token.isContextHolderRefreshRequired()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Reverting to original Authentication: "
						+ token.getSecurityContext().getAuthentication());
			}

			SecurityContextHolder.setContext(token.getSecurityContext());
		}
	}

AccessDecisionManager 

AccessDecisionManager 用于鉴权。

public interface AccessDecisionManager {
	
	void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
			InsufficientAuthenticationException;
	boolean supports(ConfigAttribute attribute);
	boolean supports(Class<?> clazz);
}

 AbstractAccessDecisionManager 

public abstract class AbstractAccessDecisionManager implements AccessDecisionManager,
		InitializingBean, MessageSourceAware {
	private List<AccessDecisionVoter<?>> decisionVoters;

	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

	private boolean allowIfAllAbstainDecisions = false;
}
    public boolean supports(ConfigAttribute attribute) {
		for (AccessDecisionVoter voter : this.decisionVoters) {
			if (voter.supports(attribute)) {
				return true;
			}
		}

		return false;
	}


	public boolean supports(Class<?> clazz) {
		for (AccessDecisionVoter voter : this.decisionVoters) {
			if (!voter.supports(clazz)) {
				return false;
			}
		}

		return true;
	}

AccessDecisionVoter

投票。即权限判断。

public interface AccessDecisionVoter<S> {
    //投票结果:
	int ACCESS_GRANTED = 1;//通过。
	int ACCESS_ABSTAIN = 0;//弃权。
	int ACCESS_DENIED = -1;//反对

	boolean supports(ConfigAttribute attribute);

	boolean supports(Class<?> clazz);
	
	int vote(Authentication authentication, S object,			Collection<ConfigAttribute> attributes);
}

 WebExpressionVoter

WebExpressionVoter ,它能理解怎样解析 SpEL 表达式。 WebExpressionVoter 借助于WebSecurityExpressionHandler 接口的一个实现类来达到这个目的。

public int vote(Authentication authentication, FilterInvocation fi,
			Collection<ConfigAttribute> attributes) {
		assert authentication != null;
		assert fi != null;
		assert attributes != null;

		WebExpressionConfigAttribute weca = findConfigAttribute(attributes);

		if (weca == null) {
			return ACCESS_ABSTAIN;
		}

		EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication,
				fi);
		ctx = weca.postProcess(ctx, fi);

		return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED
				: ACCESS_DENIED;
	}

RoleVoter 

基于角色的鉴权,权限以ROLE_开头。

AuthenticatedVoter

根据认证结果鉴权。RememberMe会使用到。

    public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY";
	public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED";
	public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY";

AffirmativeBased

AccessDecisionManager的默认实现。只要有一个通过则通过。没有反对的也通过。

public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
		int deny = 0;

		for (AccessDecisionVoter voter : getDecisionVoters()) {
			int result = voter.vote(authentication, object, configAttributes);

			if (logger.isDebugEnabled()) {
				logger.debug("Voter: " + voter + ", returned: " + result);
			}

			switch (result) {
			case AccessDecisionVoter.ACCESS_GRANTED:
				return;

			case AccessDecisionVoter.ACCESS_DENIED:
				deny++;

				break;

			default:
				break;
			}
		}

		if (deny > 0) {
			throw new AccessDeniedException(messages.getMessage(
					"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
		}

		// To get this far, every AccessDecisionVoter abstained
		checkAllowIfAllAbstainDecisions();
	}


ConsensusBased

同意大于反对,或者同意等于反对(如果允许)则通过。

UnanimousBased

只要有一个不通过,就拒绝。

密码验证时序图

猜你喜欢

转载自blog.csdn.net/demon7552003/article/details/107242436