Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
类图
为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类
概述
核心验证器
AuthenticationManager
该对象提供了认证方法的入口,接收一个Authentiaton
对象作为参数;
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
ProviderManager
它是 AuthenticationManager
的一个实现类,提供了基本的认证逻辑和方法;它包含了一个 List<AuthenticationProvider>
对象,通过 AuthenticationProvider 接口来扩展出不同的认证提供者(当Spring Security
默认提供的实现类不能满足需求的时候可以扩展AuthenticationProvider
覆盖supports(Class<?> authentication)
方法);
验证逻辑
AuthenticationManager
接收 Authentication
对象作为参数,并通过 authenticate(Authentication)
方法对其进行验证;AuthenticationProvider
实现类用来支撑对 Authentication
对象的验证动作;UsernamePasswordAuthenticationToken
实现了 Authentication
主要是将用户输入的用户名和密码进行封装,并供给 AuthenticationManager
进行验证;验证完成以后将返回一个认证成功的 Authentication
对象;
Authentication
Authentication
对象中的主要方法
public interface Authentication extends Principal, Serializable {
//#1.权限结合,可使用AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_ADMIN")返回字符串权限集合
Collection<? extends GrantedAuthority> getAuthorities();
//#2.用户名密码认证时可以理解为密码
Object getCredentials();
//#3.认证时包含的一些信息。
Object getDetails();
//#4.用户名密码认证时可理解时用户名
Object getPrincipal();
#5.是否被认证,认证为true
boolean isAuthenticated();
#6.设置是否能被认证
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
ProviderManager
ProviderManager
是AuthenticationManager
的实现类,提供了基本认证实现逻辑和流程;
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
//#1.获取当前的Authentication的认证类型
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
//#2.遍历所有的providers使用supports方法判断该provider是否支持当前的认证类型,不支持的话继续遍历
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;