登录 token 生成源码解析

依赖版本
PIGX5.6
架构模式微服务
sequenceDiagram
    participant User as User
    participant UI as pigx-ui
    participant Gateway as pigx-gateway
    participant Auth as pigx-auth
    participant UPMS as pigx-upms

    User->>UI: 提交登录请求
    UI->>Gateway: 加密报文
    Gateway->>Auth: 路由转发请求
    Auth->>Auth: 请求验证码校验
    Auth->>Auth: 解密请求报文
    Auth->>UPMS: 查询用户信息
    UPMS-->>Auth: 返回用户数据
    Auth-->>Gateway: 返回 token
    Gateway-->>UI: 返回 token

登录页面

前端账号密码登录入口文件位于 src/views/login/component/password.vue,此文件涉及页面验证码获取逻辑,行为验证码基于 AJ-Captcha 实现。

密码登录页面逻辑

密码登录页面逻辑流程

前端登录成功后跳转逻辑

登录成功跳转逻辑

前端报文加密

报文加密说明

为了保证前端提交登录信息(尤其是密码字段)在传输过程中不被抓包破解,PIGX 框架对登录报文进行了对称加密处理,详细说明参考:前端报文加密的业务

加密示例:

password 明文:123456
转成
password 密文:JFat0Zdc
前端报文加密流程

前端提供简单的 AES 对称加密算法,注意 key 和后端网关配置相同,这里打包混淆后,相对安全。

AES 加密实现

网关请求转发

路由转发核心功能

网关中最重要的功能是路由转发,根据请求前缀匹配到对应服务。例如所有以 /auth 开头的请求会自动转发至 pigx-auth 服务的接口处理。

以下为 sys_route_conf 表定义的路由规则:

路由规则配置表
路由前缀截取

在 PigxRequestGlobalFilter 过滤器中另外一个重要的功能就是截取路由前缀。

示例:

http://127.0.0.1:9999/auth/oauth2/token
转发到 pigx-auth 的请求路径自动截取前缀变成
http://127.0.0.1:3000/oauth2/token
路由前缀截取逻辑

验证码校验处理

验证码校验流程

pigx-auth 认证中心在接收到网关转发的请求后,会通过 ValidateCodeFilter 对验证码(图形、短信)等进行校验。

前端请求密文解密

密文解密处理

pigx-auth 在处理完验证码判断逻辑后,PasswordDecoderFilter 针对 /oauth2/token 请求会进行前端密码解密。

解密示例:

password 密文:JFat0Zdc
转成
password 明文:123456

auth 模块详解

auth 模块架构图

登录请求报文示例:

POST /auth/oauth2/token?grant_type=password&scope=server HTTP/1.1
Host: pig-gateway:9999
Authorization: Basic dGVzdDp0ZXN0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
username=admin&password=YehdBPev

客户端认证

客户端认证流程

在登录请求中会携带 Basic base64(clientId:clientSecret)OAuth2ClientAuthenticationFilter 会通过调用 RegisteredClientRepository(数据库存储)来判断传入的客户端是否正确。

客户端认证代码

接收登录请求

OAuth2TokenEndpointFilter 会接收通过上文 OAuth2ClientAuthenticationFilter 客户端认证的请求。

接收登录请求流程

组装认证对象

AuthenticationConverter 会根据请求中的参数和授权类型组装成对应的授权认证对象。

组装认证对象

登录认证对象

AuthenticationToken 继承关系:

public class XXXAuthenticationToken extends OAuth2ResourceOwnerBaseAuthenticationToken {

}
认证对象类图

授权认证调用

授权认证调用流程

核心认证逻辑

核心认证逻辑流程

多用户体系匹配 UserDetailsService

多用户体系匹配

密码匹配校验

密码匹配校验

用户状态校验

用户状态校验

用户查询逻辑

用户查询逻辑的多种实现形式:

  • 解耦:通过 feign 查询其他系统获取并组装成 UserDetails
  • 简单:认证中心直接查询 DB 并组装成 UserDetails
用户查询逻辑实现

密码校验逻辑

密码校验逻辑 PasswordEncoder 实现
密码加密方式

PasswordEncoder 会自动根据特征码匹配对应的加密算法,因此在查询用户对象组装成 UserDetails 时需要添加加密特征码前缀。

默认支持加密方式:

{noop}密码明文
{加密特征码}密码密文

代码示例:

return new UserDetails(user.getUsername(), "{bcrypt}" + "数据库存储的密文");

生成 OAuth2AccessToken

生成 AccessToken

Token 存储持久化

Token 存储架构
存储方式扩展

当前 Spring Authorization Server 仅支持 JDBC 和内存存储,PIGX 扩展支持 Redis 实现。

Redis 存储实现

登录成功事件处理

事件驱动扩展

基于 SpringEvent 事件处理,可以在这里做更多的处理,如日志记录、个性化配置等。

登录成功事件处理

请求结果输出 Token

private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response,
                                     Authentication authentication) throws IOException {

    OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication;

    OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
    OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
    Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
    // 无状态 注意删除 context 上下文的信息
    SecurityContextHolder.clearContext();
    this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
}

定义具体的输出返回格式等逻辑:

Token 输出格式定义