1. Spring-Security 核心类图

2. 核心类说明
2.1. SecurityContextHolder
SecurityContextHolder 是用来保存 SecurityContext 的。SecurityContext 中含有当前正在访问系统的用户的详细信息。默认情况下,SecurityContextHolder 将使用 ThreadLocal 来保存 SecurityContext,这也就意味着在处于同一线程中的方法中我们可以从 ThreadLocal 中获取到当前的 SecurityContext。因为线程池的原因,如果我们每次在请求完成后都将 ThreadLocal 进行清除的话,那么我们把 SecurityContext 存放在 ThreadLocal 中还是比较安全的。这些工作 Spring Security 已经自动为我们做了,即在每一次 request 结束后都将清除当前线程的 ThreadLocal。
SecurityContextHolder 中定义了一系列的静态方法,而这些静态方法内部逻辑基本上都是通过 SecurityContextHolder 持有的 SecurityContextHolderStrategy 来实现的,如 getContext()、setContext()、clearContext()等。而默认使用的 strategy 就是基于 ThreadLocal 的。而默认使用的 strategy 就是基于 ThreadLocal 的 ThreadLocalSecurityContextHolderStrategy。除了ThreadLocalSecurityContextHolderStrategy,Spring-security还定义了两种类型的Strategy实现。
2.1.1. GlobalSecurityContextHolderStrategy
- 表示全局使用同一个 SecurityContext,如 C/S 结构的客户端
2.1.2. InheritableThreadLocalSecurityContextHolderStrategy
- InheritableThreadLocal 来存放 SecurityContext,即子线程可以使用父线程中存放的变量。
一般而言,我们使用默认的 strategy 就可以了,但是如果要改变默认的 strategy,Spring Security 为我们提供了两种方法,这两种方式都是通过改变 strategyName 来实现的。SecurityContextHolder 中为三种不同类型的 strategy 分别命名为 MODE_THREADLOCAL、MODE_INHERITABLETHREADLOCAL 和 MODE_GLOBAL。第一种方式是通过 SecurityContextHolder 的静态方法 setStrategyName() 来指定需要使用的 strategy;第二种方式是通过系统属性进行指定,其中属性名默认为 “spring.security.strategy”,属性值为对应 strategy 的名称。
2.2. Authentication
Authentication 是一个接口,用来表示用户认证信息的,在用户登录认证之前相关信息会封装为一个 Authentication 具体实现类的对象,在登录认证成功之后又会生成一个信息更全面,包含用户权限等信息的 Authentication 对象,然后把它保存在 SecurityContextHolder 所持有的 SecurityContext 中,供后续的程序进行调用,如访问权限的鉴定等。
Spring Security 使用一个 Authentication 对象来描述当前用户的相关信息。SecurityContextHolder 中持有的是当前用户的 SecurityContext,而 SecurityContext 持有的是代表当前用户相关信息的 Authentication 的引用。这个 Authentication 对象不需要我们自己去创建,在与系统交互的过程中,Spring Security 会自动为我们创建相应的 Authentication 对象,然后赋值给当前的 SecurityContext。但是往往我们需要在程序中获取当前用户的相关信息,比如最常见的是获取当前登录用户的用户名。在程序的任何地方,通过如下方式我们可以获取到当前用户的用户名。
public String getCurrentUsername() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
}
if (principal instanceof Principal) {
return ((Principal) principal).getName();
}
return String.valueOf(principal);
}
此外,调用 SecurityContextHolder.getContext() 获取 SecurityContext 时,如果对应的 SecurityContext 不存在,则 Spring Security 将为我们建立一个空的 SecurityContext 并进行返回。
2.3. AuthenticationManager 和 AuthenticationProvider
Spring-Security 最核心的接口是AuthenticationManager。AuthenticationManager 是一个用来处理认证(Authentication)请求的接口。在其中只定义了一个方法 authenticate(),该方法只接收一个代表认证请求的 Authentication 对象作为参数,如果认证成功,则会返回一个封装了当前用户权限等信息的 Authentication 对象进行返回。
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
- 在 Spring Security 中,AuthenticationManager 的默认实现是 ProviderManager,而且它不直接自己处理认证请求,而是委托给
AuthenticationManagerDelegator其所配置的 AuthenticationProvider 列表,然后会依次使用每一个 AuthenticationProvider 进行认证。如果有一个 AuthenticationProvider 认证后的结果不为 null,则表示该 AuthenticationProvider 已经认证成功,之后的 AuthenticationProvider 将不再继续认证。
public class ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean {
private List<AuthenticationProvider> providers = Collections.emptyList();
private AuthenticationManager parent;
public ProviderManager(List<AuthenticationProvider> providers) {
this(providers, null);
}
public ProviderManager(List<AuthenticationProvider> providers,
AuthenticationManager parent) {
Assert.notNull(providers, "providers list cannot be null");
this.providers = providers;
this.parent = parent;
checkState();
}
}
从代码中可以看到ProviderManager内部是维护了一个List<AuthenticationProvider>,AuthenticationManager自己是一个接口不干活,靠他的实现类ProviderManager去认证请求,他自己又不干活,交给内部的一个AuthenticationProvider列表去认证请求。绕是绕,但是有它的道理,学过shiro的同学可能知道realm这个东西,就跟spring-security中的AuthenticationProvider类似,设计成一个列表,意思是一个用户可能用多种方式进行认证(邮箱登录,手机号码登录,指纹登录?,第三方登录等等乱编的登录)。只需要通过一个即可认证成功即可.如果所有的 AuthenticationProvider 的认证结果都为 null,则表示认证失败,将抛出一个 ProviderNotFoundException。校验认证请求最常用的方法是根据请求的用户名加载对应的 UserDetails,然后比对 UserDetails 的密码与认证请求的密码是否一致,一致则表示认证通过。Spring Security 内部的 DaoAuthenticationProvider 就是使用的这种方式。其内部使用 UserDetailsService 来负责加载 UserDetails,UserDetailsService 将在下节讲解。在认证成功以后会使用加载的 UserDetails 来封装要返回的 Authentication 对象,加载的 UserDetails 对象是包含用户权限等信息的。认证成功返回的 Authentication 对象将会保存在当前的 SecurityContext 中。
本文详细介绍了Spring-Security的核心类,包括SecurityContextHolder、Authentication、AuthenticationManager和AuthenticationProvider。SecurityContextHolder使用ThreadLocal保存SecurityContext,提供全局访问用户认证信息的方式。Authentication接口表示用户认证信息,AuthenticationManager通过AuthenticationProvider列表处理认证请求,实现灵活的认证策略。
1844

被折叠的 条评论
为什么被折叠?



