Springboot 系列:SpringSecurity 认证
在之前的文章中讲述了《SpringSecurity 整体架构》,这篇文章承接上篇文章,主要讲述 SpringSecurity 的认证流程及相关的类。
整体流程
我们以之前文章的代码来分析认证的整体流程,关键步骤如下所示: 
登陆主要流程:
- 用户首先打开登陆页面,页面的 endpoint 为
login.html, Method: GET, 该页面直接放行,返回给浏览器渲染展现; - 用户输入用户名/密码,执行登陆操作,endpoint 为
login, Method: POST, 而login是我们指定的登陆处理 endpoint; login, Method: POST请求被 UsernamePasswordAuthenticationFilter 拦截,读取参数构造请求对象 UsernamePasswordAuthenticationToken;- 将 UsernamePasswordAuthenticationToken 对象传递给 AuthenticationManager 进行认证,获取 Authentication 对象;
- AuthenticationManager 是一个接口,在这里使用的是其实现类 ProviderManager, ProviderManager 对象中包含有一组 AuthenticationProvider, 每一个 AuthenticationProvider 代表了一类认证场景,我们可以根据需求自定义 AuthenticationProvider 类实现我们的业务;
- AuthenticationProvider 的主要功能是将查询用户 UserDetails 对象并将其映射为 Authentication 对象,而每一个 AuthenticationProvider 对象只会针对特定的请求对象进行处理,在这里,由于我们的请求对象是 UsernamePasswordAuthenticationToken, 对应地, AuthenticationProvider 的实现类使用的是 DaoAuthenticationProvider;
- DaoAuthenticationProvider 需要获用户的信息,是通过 UserDetailsService 接口来实现的,在这里,我们自定义了 CustomerUserDetailsService 对象,实现从数据库中获取用户信息 UserDetails 对象,而 UserDetails 也是一个接口,需要我们自定义实现;
- 在 DaoAuthenticationProvider 中获取用户信息 UserDetails 对象,需要将其转化为 Authentication 对象;
- ProviderManager 对象中获取到 Authentication 对象之后,进行复制操作之后,返回给 UsernamePasswordAuthenticationFilter 对象。这个 Authentication 对象持有用户的账号及权限信息,在后期的授权操作中会被用到。
登出主要流程:
- 登出的 endpoint 为
logout, Method: GET, 该 endpoint 被 LogoutFilter 拦截; - 在 LogoutFilter 中,登出的业务逻辑封装在 LogoutHandler 对象中,会执行会话的清理操作;
- 操作成功之后再跳转到登出成功页面。
认证相关的类
- AuthenticationManager: 认证的核心接口,输入为用户请求对象,如 UsernamePasswordAuthenticationToken,输出为认证对象 Authentication;
- ProviderManager: AuthenticationManager 接口实现类,它包含一组 AuthenticationProvider 对象,而每一个 AuthenticationProvider 代表了一类认证场景,它对应只会处理对应的请求对象;
- DaoAuthenticationProvider: AuthenticationProvider 接口实现类,主要功能是从 UserDetailsService 接口获取用户对象 UserDetails, 将转换为 Authentication 对象;
- UserDetailsService:用户查询接口,根据用户名查询用户信息;
- CustomerUserDetailsService:UserDetailsService 实现类,从数据库中获取用户信息;
- UserDetails: 用户信息接口,包括了获取用户名及权限的方法;
- Authentication: 认证对象对象;
用户模型
在 SpringSecurity 中,定义了 UserDetails 及 Authentication 接口,用于存储用户及认证数据。通过 UserDetails 接口,业务系统可以将用户信息传递给 SpringSecurity, 然后封装成 Authentication, 供后续功能使用。
UserDeails 接口
1 | public interface UserDetails extends Serializable { |
UserDetails 提供了访问用户名称、状态及权限相关的方法,上层业务需要实现自己的 UserDetails 类,如下所示:
1 | public class User implements UserDetails, Serializable { |
UserDetails 接口会包含一组权限,SpringSecurity
也定义了权限接口,如下所示: 1
2
3
4
5
6
7
8
9
10public interface GrantedAuthority extends Serializable {
/**
* 获取权限
*
* @return 权限
*/
String getAuthority();
}
在 GrantedAuthority
接口中,只是简单定义了返回权限名称的方法,业务上层可以进行扩展,用户的角色也包含在权限表中,使用
ROLE_ 前缀来区分。
Authentication 接口
Authentication 接口存储认证成功的信息,它由 UserDetails 封装转换而来。
1 | public interface Authentication extends Principal, Serializable { |
在 Authentication 中,将 Principal 定义为用户信息,Credentials 为密码信息,GrantedAuthority 为权限信息,另外还保存了请求对象详情信息。这些信息从 UserDetails, UsernamePasswordAuthenticationToken 中获取到。
1 | public Authentication authenticate(Authentication authentication) |
通过代码可知:
- UserDetails ---> principal
- UserDetails.getAuthorities() ---> list of GrantedAuthority
- requestAuthentication.getCredentials() ---> credentials
- requestAuthentication..getDetails() ---> details
默认设置
Endpoints SpringSecurity 内置登陆/登出相关的 Endpoint, 用户不做任何配置即可使用,这些 Endpoint 包括:
- /login,GET: 登陆页面,默认由 DefaultLoginPageGeneratingFilter 对象生成;
- /login,POST: 对用户进行认证, 传入参数
username和password生成 UsernamePasswordAuthenticationToken, 这两个参数名称可以修改; - /login?error: 登陆出错页面;
- /login?logout: 成功登陆退出后跳转的页面。
默认参数 在登陆页面表单中需要配置用户名称及密码,它们的默认名称如下:
- username: 用户名称;
- password: 用户密码。
这些默认配置都可以在 FormLoginConfigurer 中配置:
1 | FormLoginConfigurer |
参考: