目录
UsernamePasswordAuthenticationToken
AbstractOAuth2TokenAuthenticationToken
![](/qrcode.jpg)
AbstractUserDetailsAuthenticationProvider
DaoAuthenticationProvider.retrieveUser()
additionalAuthenticationChecks()
UsernamePasswordAuthenticationFilter
AbstractAuthenticationProcessingFilter
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
AbstractUserDetailsAuthenticationProvider
为DaoAuthenticationProvider
提供了基本的认证方法。
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
与特定用户信息来源进行解耦的地方,UserDetailsService
由UserDetails
和UserDetailsManager
所构成;UserDetails
和UserDetailsManager
各司其责,一个是对基本用户信息进行封装,一个是对基本用户信息进行管理;
特别注意
,UserDetailsService
、UserDetails
以及UserDetailsManager
都是可被用户自定义的扩展点,我们可以继承这些接口提供自己的读取用户来源和管理用户的方法,比如我们可以自己实现一个 与特定 ORM 框架,比如 Mybatis 或者 Hibernate,相关的UserDetailsService
和UserDetailsManager
;
引入
@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
只要有一个不通过,就拒绝。