前面的简单的认证流程中,是可以将一些东西抽象出来作为一个单独的实体,这些实体都可以在 Spring Security 中找到相应的对象,包括:
用户输入的账号密码等信息,这些东西其实就是用户的认证信息,对应到 Spring Security 中的话就是 Authentication
对象,只不过,Spring Security 中的 Authentication 对象除了保存用户的认证信息以外,还可以用来保存用户认证成功后从数据库中拿到的用户的详细信息。
public interface Authentication extends Principal, Serializable { Collection<? extends GrantedAuthority> getAuthorities(); // 用户权限 Object getCredentials(); // 用户认证信息 Object getDetails(); // 用户详细信息 Object getPrincipal(); // 用户身份信息 boolean isAuthenticated(); // 当前 Authentication 是否已认证 void setAuthenticated(boolean isAuthenticated); }
只凭用户提供的认证信息往往是不足以用来判断该用户是否合法的,因此,我们通常还需要某种手段来获取保存在服务端的用户信息,同时,也需要某种手段来保存用户信息,这些对应到 Spring Security 中的话就是 UserDetailsService
和 UserDetails
这两个对象。
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; } public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }
在拥有了用户提供的认证信息和保存在服务端的用户信息后,我们就需要通过某种方式来比较这两份信息,而这种来效验用户认证信息的对象对应到 Spring Security 中便是 AuthenticationManager
对象了。
Authentication authenticate(Authentication authentication)throws AuthenticationException;
而鉴于各种各样的用户认证信息和层出不穷的效验方式,Spring Security 提供了更易于我们扩展的接口 AuthenticationProvider
和 ProviderManager
这个默认的 AuthenticationManager 实现。使用时,我们往往就只需要实现 AuthenticationProvider
就足够了。
public interface AuthenticationProvider { Authentication authenticate(Authentication authentication) throws AuthenticationException; boolean supports(Class<?> authentication); }
可以看到,AuthenticationProvider 中的方法 authenticate
会返回一个 Authentication
对象,当通过认证后,这个对象往往会保存用户的详细信息。
当用户的认证信息通过效验后,我们往往还需要在服务端保存通过的认证信息或生成的令牌,这个保存通过的认证信息的对象在 Spring Security 中就是 SecurityContext
和 SecurityContextHolder
这两个对象, SecurityContext 保存已通过认证的 Authentication 对象,SecurityContextHolder 保存 SecurityContext 到当前线程的上下文,方便我们的使用。
public interface SecurityContext extends Serializable { Authentication getAuthentication(); void setAuthentication(Authentication authentication); } public class SecurityContextHolder { public static SecurityContext getContext(); public void SecurityContext setContext(); }
Spring Security 是基于 Filter 来实现的,而每个请求往往也会分配一个线程,因此,Spring Security 在请求到达具体的处理逻辑之前,就可以在 Filter 中完成用户信息的认证,生成 SecurityContext 方面后续的使用。
这里在附上一张来源于 Spring Security(一) —— Architecture Overview | 芋道源码 —— 纯源码解析博客 的一张图,很好的解释了上述对象之间的关系:
可以看到,虽然 Spring Security 看似很复杂,但是其核心思想和以前那种简单的认证流程依然是一样的。只不过,Spring Security 将其中的关键部分抽象了处理,又提供了相应的扩展接口。
我们在使用时,便可以实现自己的 UserDetailsService 和 UserDetails 来获取保存用户信息,实现自己的 Authentication 来保存特定的用户认证信息,实现自己的 AuthenticationProvider 使用自己的 UserDetailsService 和 Authentication 来对用户认证信息进行效验。
当然了,Spring Security 还存在更多的功能,但是,在对基本流程有了一定的理解后,后续的内容也就能够更加容易的进行学习了。