通过SecurityContextHolder.getContext()获得SecurityContext
总接口SecurityContextHolderStrategy
private static void initialize() {
if ((strategyName == null) || "".equals(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
} else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
} else if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
} else {
// Try to load a custom strategy
try {
Class clazz = Class.forName(strategyName);
Constructor customStrategy = clazz.getConstructor(new Class[] {});
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance(new Object[] {});
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
initializeCount++;
}
当SecurityContextHolder初始化的时候。判断调用哪个SecurityContextHolderStrategy的实现类
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
让后通过 SecurityContext 接口 实现类 SecurityContextImpl 获得 getAuthentication()的到Authentication 对象,Authentication 对象中装了很多用户信息
Authentication 主要是包括一些用户认证的信息,比如权限啊。名字啊什么的。。。
Springsecurity 主要是 认证+授权+filter
认证 分 证管理器+认证者。
认证管理器(org.springframework.security.AuthenticationManager接口
public class ProviderManager extends AbstractAuthenticationManager implements InitializingBean, MessageSourceAware,
ApplicationEventPublisherAware {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(ProviderManager.class);
private static final Properties DEFAULT_EXCEPTION_MAPPINGS = new Properties();
//~ Instance fields ================================================================================================
private ApplicationEventPublisher applicationEventPublisher;
private ConcurrentSessionController sessionController = new NullConcurrentSessionController();
private List providers;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private Properties exceptionMappings = new Properties();
private Properties additionalExceptionMappings = new Properties();
<!-- 认证管理器(org.springframework.security.AuthenticationManager接口) org.springframework.security.providers.ProviderManager是认证管理器的一个实现, ProviderManager通过遍历一个提供者的集合来实现身份验证, 直到某一个认证提供者能够成功地验证该用户的身份 --> <!-- 通过Providers提供认证者列表,如果一个认证提供者失败可以尝试另外一个认证提供者,以保证获取不同来源的身份认证,如 DaoAuthenticationProvider 从数据库中读取用户信息验证身份 AnonymousAuthenticationProvider 匿名用户身份认证 RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证 其它的还有 AuthByAdapterProvider 使用容器的适配器验证身份 CasAuthenticationProvider 根据Yale中心认证服务验证身份, 用于实现单点登陆 JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证身份 RemoteAuthenticationProvider 根据远程服务验证用户身份 RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证 X509AuthenticationProvider 从X509认证中获取用户信息验证身份 TestingAuthenticationProvider 单元测试时使用 每个认证者会对自己指定的证明信息进行认证,如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证。 总接口 AuthenticationManager 管理器 装着 很多个AuthenticationProvider 管理者--> <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager" p:sessionController-ref="concurrentSessionController"> <!-- private List providers;<装着很多个认证器> --> <property name="providers"> <list> <ref bean="daoAuthenticationProvider"/> <bean class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider" p:key="springsecurity"/> <bean class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider" p:key="springsecurity"/> </list> </property> </bean>
可以看出一个认证器里面包含了多个认证者
下面来看看认证者接口
public interface AuthenticationProvider {
//~ Methods ========================================================================================================
/**
* Performs authentication with the same contract as {@link
* org.springframework.security.AuthenticationManager#authenticate(Authentication)}.
*
* @param authentication the authentication request object.
*
* @return a fully authenticated object including credentials. May return <code>null</code> if the
* <code>AuthenticationProvider</code> is unable to support authentication of the passed
* <code>Authentication</code> object. In such a case, the next <code>AuthenticationProvider</code> that
* supports the presented <code>Authentication</code> class will be tried.
*
* @throws AuthenticationException if authentication fails.
*/
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
有一个认证方法。不同的实现类通过这个方法来认证用户。下面来看下DaoAuthenticationProvider认证者是怎么认证用户的。
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
//~ Instance fields ================================================================================================
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
private SaltSource saltSource;
private UserDetailsService userDetailsService;
private boolean includeDetailsObject = true;
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
}
catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
DaoAuthenticationProvider认证者通过注入UserDetailsService来获取用户信息
<!-- 认证管理器 总接口AuthenticationProvider -->
<bean id="daoAuthenticationProvider"
class="org.springframework.security.providers.dao.DaoAuthenticationProvider"
p:passwordEncoder-ref="passwordEncoder"
p:userDetailsService-ref="userDetailsService"/>
UserDetailsService 就相当于我们的业务逻辑层 通过不同的方式获取用户信息 比如:
<!-- 获得userDetails的service 总接口UserDetailsService 子类-->
<bean id="userDetailsService"
class="org.springframework.security.userdetails.memory.InMemoryDaoImpl">
<property name="userProperties">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/users.properties"/>
</property>
通过properties文件来配置用户权限
admin=admin,ROLE_SUPERVISOR
user1=user1,ROLE_USER
user2=user2,ROLE_USER
user3=user3,disabled,ROLE_USER
#scott/wombat
scott=2b58af6dddbd072ed27ffc86725d7d3a,ROLE_USER
还可以这样:
<property name="userMap">
<value>
admin=admin,ROLE_SUPERVISOR
user1=user1,ROLE_USER
user2=user2,ROLE_USER
user3=user3,disabled,ROLE_USER
</value>
</property>
至于org.springframework.security.userdetails.memory.InMemoryDaoImpl 怎么封装,可想而知
*/
public class InMemoryDaoImpl implements UserDetailsService, InitializingBean {
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.userMap,
"A list of users, passwords, enabled/disabled status and their granted authorities must be set");
}
public UserMap getUserMap() {
return userMap;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
return userMap.getUser(username);
}
public void setUserMap(UserMap userMap) {
this.userMap = userMap;
}
public void setUserProperties(Properties props) {
UserMap userMap = new UserMap();
this.userMap = UserMapEditor.addUsersFromProperties(userMap, props);
}
}
下面来看这个service有多少中实现方式。实现类
看实现类名字就可以想到。可以通过jdbc配置在数据库中,InMemoryDaoImpl配置文件中(加载到内存)
可以把用户信息放到Ldap。 UserDetailsService 通过loadUserByUsername方法加载用户信息
class CachingUserDetailsService implements UserDetailsService {
private UserCache userCache = new NullUserCache();
private UserDetailsService delegate;
CachingUserDetailsService(UserDetailsService delegate) {
this.delegate = delegate;
}
public UserCache getUserCache() {
return userCache;
}
public void setUserCache(UserCache userCache) {
this.userCache = userCache;
}
public UserDetails loadUserByUsername(String username) {
UserDetails user = userCache.getUserFromCache(username);
if (user == null) {
user = delegate.loadUserByUsername(username);
}
Assert.notNull(user, "UserDetailsService " + delegate + " returned null for username " + username + ". " +
"This is an interface contract violation");
userCache.putUserInCache(user);
return user;
}
}
还可以为实现类注入缓存默认是private UserCache userCache = new NullUserCache();
下面来看下springsecurity的缓存
springsecurity的缓存主要还是通过ehcache缓存实现的。只是封装了一下方法。下面请看UserCache实现类
public class EhCacheBasedUserCache implements UserCache, InitializingBean {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(EhCacheBasedUserCache.class);
//~ Instance fields ================================================================================================
private Ehcache cache;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "cache mandatory");
}
public Ehcache getCache() {
return cache;
}
public UserDetails getUserFromCache(String username) {
Element element = null;
try {
element = cache.get(username);
} catch (CacheException cacheException) {
throw new DataRetrievalFailureException("Cache failure: " + cacheException.getMessage());
}
if (logger.isDebugEnabled()) {
logger.debug("Cache hit: " + (element != null) + "; username: " + username);
}
if (element == null) {
return null;
} else {
return (UserDetails) element.getValue();
}
}
public void putUserInCache(UserDetails user) {
Element element = new Element(user.getUsername(), user);
if (logger.isDebugEnabled()) {
logger.debug("Cache put: " + element.getKey());
}
cache.put(element);
}
public void removeUserFromCache(UserDetails user) {
if (logger.isDebugEnabled()) {
logger.debug("Cache remove: " + user.getUsername());
}
this.removeUserFromCache(user.getUsername());
}
public void removeUserFromCache(String username) {
cache.remove(username);
}
public void setCache(Ehcache cache) {
this.cache = cache;
}
}
在这里可以看到这些方法都是调用了ehcache里面的方法。 这里的 private Ehcache cache;是靠你在配置文件配的。注入进来的。
<bean class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache">
<property name="userCache" value="userCahce"></property>
</bean>
SpringSecurity刚入门不久,主要把概率,理论弄清楚了。感觉还是比较容易的。让后在结合源码看一下。映像就深刻多了,今天就写到这了。下次在看下SpringSecurity的Filter在说吧。。。。。