CachingUserDetailsService类的构造接收一个用于真正加载UserDetails的UserDetailsService实现类,当需要加载UserDetails时,会首先从缓存中获取,如果缓存中没有UserDetails存在,则使用持有的UserDetailsService实现类进行加载,然后将加载后的结果存在缓存中,UserDetails与缓存的交互是通过UserCache接口来实现的,CachingUserDetailsService默认拥有一个UserCache的NullUserCache()实现。Spring Security提供的缓存都是基于内存的缓存,并且缓存的UserDetails对象,在实际应用中一般会用到更多的缓存,比如Redis,同时也会对权限相关的信息等更多的数据进行缓存。
2.5 自定义决策 Spring Security在用户身份认证通过后,会调用一个角色管理器判断是否可以继续访问,[Spring Security核心处理流程(图1-5)](#1.3 数据库管理)中的AccessDecisionManager就是Spring Security的角色管理器,它对应的抽象类为AbstractAccessDecisionManager,要自定义决策管理器的话一般是继承这个抽象类,而不是去实现接口。
package org.springframework.security.access.vote; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.util.Assert; /** * Abstract implementation of {@link AccessDecisionManager}. * * <p> * Handles configuration of a bean context defined list of {@link AccessDecisionVoter}s * and the access control behaviour if all voters abstain from voting (defaults to deny * access). */ public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean, MessageSourceAware { protected final Log logger = LogFactory.getLog(getClass()); private List<AccessDecisionVoter<?>> decisionVoters; protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private boolean allowIfAllAbstainDecisions = false; protected AbstractAccessDecisionManager( List<AccessDecisionVoter<?>> decisionVoters) { Assert.notEmpty(decisionVoters, "A list of AccessDecisionVoters is required"); this.decisionVoters = decisionVoters; } public void afterPropertiesSet() { Assert.notEmpty(this.decisionVoters, "A list of AccessDecisionVoters is required"); Assert.notNull(this.messages, "A message source must be set"); } protected final void checkAllowIfAllAbstainDecisions() { if (!this.isAllowIfAllAbstainDecisions()) { throw new AccessDeniedException(messages.getMessage( "AbstractAccessDecisionManager.accessDenied", "Access is denied")); } } public List<AccessDecisionVoter<?>> getDecisionVoters() { return this.decisionVoters; } public boolean isAllowIfAllAbstainDecisions() { return allowIfAllAbstainDecisions; } public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) { this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions; } public void setMessageSource(MessageSource messageSource) { this.messages = new MessageSourceAccessor(messageSource); } public boolean supports(ConfigAttribute attribute) { for (AccessDecisionVoter voter : this.decisionVoters) { if (voter.supports(attribute)) { return true; } } return false; } /** * Iterates through all <code>AccessDecisionVoter</code>s and ensures each can support * the presented class. * <p> * If one or more voters cannot support the presented class, <code>false</code> is * returned. * * @param clazz the type of secured object being presented * @return true if this type is supported */ public boolean supports(Class<?> clazz) { for (AccessDecisionVoter voter : this.decisionVoters) { if (!voter.supports(clazz)) { return false; } } return true; } }