Spring Security--学习记录二
Spring Security认证流程图
1.Http Request
Spring security 定义了一个过滤器链, 当认证请求到达这个链时, 该请求将会穿过这个链条用于认证和授权. 这个链上的可以定义1..N个过滤器, 过滤器的用途是获取请求中的认证信息, 根据认证方式进行路由, 把认证信息传递给对应的认证处理程序进行处理.
常用过滤链
不同的过滤器处理不同的认证信息. 例如:
- HTTP Basic 认证请通过过滤器链, 到达 BasicAuthenticationFilter
- HTTP Digest 认证被 DigestAuthenticationFilter 识别,拦截并处理.
- 表单登录认证被 UsernamePasswordAuthenticationFilter 识别,拦截并处理.
2.基于用户凭证创建 AuthenticationToken
多种类型的 AuthenticationToken, 基于不同的认证方式, 过滤器会创建不同类型的 AuthenticationToken
这里我们以最常用表单登录为例子, 用户在登录表单中输入用户名和密码, 并点击确定, 浏览器提交POST请求到服务器, 穿过过滤器链, 被 UsernamePasswordAuthenticationFilter 识别, UsernamePasswordAuthenticationFilter 提取请求中的用户名和密码来创建 UsernamePasswordAuthenticationToken 对象.
3.把组装好的 AuthenticationToken 传递给 AuthenticationManagager
组装好的 UsernamePasswordAuthenticationToken 对象被传递给 AuthenticationManagager 的 authenticate 方法进行认证决策.AuthenticationManager 只是一个接口, 实际的实现是 ProviderManager
ProviderManager 有一个配置好的认证提供者列表(AuthenticationProvider), ProviderManager 会把收到的 UsernamePasswordAuthenticationToken 对象传递给列表中的每一个 AuthenticationProvider 进行认证.
4.进行认证处理
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
boolean supports(Class<?> authentication);
}
AuthenticationProvider提供了以下的实现类:
CasAuthenticationProvider JaasAuthenticationProvider DaoAuthenticationProvider OpenIDAuthenticationProvider RememberMeAuthenticationProvider LdapAuthenticationProvider
上面我们说了, ProviderManager 会把收到的 UsernamePasswordAuthenticationToken 对象传递给列表中的每一个 AuthenticationProvider 进行认证.那到底 UsernamePasswordAuthenticationToken 会被哪一个接收和处理呢?是由supports方法来决定的。
5.UserDetailsService获取用户信息
UserDetailsService 获取的对象是一个 UserDetails. 框架中自带一个 User 实现, 但是一般我们需要对 UserDetails 进行定制, 内置的 User 太过简单实际项目无法满足需要.案例说明(基于jpa实现):
@Service
public class JpaReactiveUserDetailsService implements ReactiveUserDetailsService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
/**
* @param s 用户名
* @return Mono<UserDetails>
*/
@Override
public Mono<UserDetails> findByUsername(String s) {
// 从用户Repository中获取一个User Jpa实体对象
Optional<User> optionalUser = userRepository.findByUsername(s);
if (!optionalUser.isPresent()) {
return Mono.empty();
}
User user = optionalUser.get();
// 填充权限
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : user.getRoles()) {
authorities.add(new (role.getName()));
}
// 返回 UserDetails
return Mono.just(new org.springframework.security.core.userdetails.User(
user.getUsername(), user.getPassword(), authorities
));
}
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
@Override
void delete(User user);
Optional<User> findByUsername(String username);
}
6.认证结果处理
如果认证成功(用户名,密码完全正确), AuthenticationProvider 将会返回一个完全有效的 Authentication 对象(UsernamePasswordAuthenticationToken). 否则抛出 AuthenticationException 异常.完全有效的 Authentication 对象定义如下:
authenticated属性为 true 已授权的权限列表(GrantedAuthority列表) 用户凭证(仅用户名)
7.认证完成
认证完成后, AuthenticationManager 将会返回该认证对象(UsernamePasswordAuthenticationToken)返回给过滤器
8.存储认证对象
相关的过滤器获得一个认证对象后, 把它存储在安全上下文中(SecurityContext) 用于后续的授权判断(比如查询,修改等操作).
SecurityContextHolder.getContext().setAuthentication(authentication);
认证架构图