登录注册加密完整说明.md 15 KB

登录注册加密完整说明文档

📋 目录

  1. 数据库表结构
  2. 密码加密机制
  3. 用户注册流程
  4. 用户登录流程
  5. JWT Token机制
  6. 代码文件位置汇总

数据库表结构

用户表 (user)

表名: user

字段说明:

字段名 类型 说明 备注
user_id INT 用户ID 主键,自增
username VARCHAR 用户名 唯一,用于登录
password VARCHAR 密码 BCrypt加密存储,格式:$2a$10$...
nickname VARCHAR 昵称 显示名称
avatar_url VARCHAR 头像URL 用户头像地址
phone VARCHAR 手机号 可选
email VARCHAR 邮箱 可选
user_role INT 用户角色 0:普通用户,1:管理员,2:超级管理员
member_level INT 会员等级 0:普通,1:VIP
create_time DATETIME 创建时间 注册时间
last_login_time DATETIME 最后登录时间 登录时更新
status INT 状态 1:正常,0:禁用
face_person_id VARCHAR 人脸识别ID 百度人脸识别
face_bind_status INT 人脸绑定状态 0:未绑定,1:已绑定
last_face_verify_time DATETIME 最后人脸验证时间 管理员人脸验证

实体类位置: book/src/main/java/com/zhentao/pojo/User.java


密码加密机制

1. 加密算法:BCrypt

为什么使用BCrypt?

  • 单向哈希:无法逆向解密,只能验证
  • 自动加盐:每次加密结果不同,但都能验证通过
  • 计算成本可调:默认强度为10,可防止暴力破解
  • 行业标准:Spring Security推荐使用

2. 加密配置

配置文件位置: book/src/main/java/com/zhentao/config/SecurityConfig.java

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

关键代码: 第16-18行

3. 密码加密格式

加密后的密码格式:$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy

  • $2a$ - BCrypt版本标识
  • 10 - 加密强度(cost factor)
  • 后面60个字符 - 盐值和哈希值

用户注册流程

完整流程图

用户提交注册信息
    ↓
Controller接收请求 (UserAuthController.register)
    ↓
检查用户名是否已存在
    ↓
使用BCrypt加密密码 (UserServiceImpl.register)
    ↓
设置默认值(角色、会员等级、状态)
    ↓
保存到数据库 (user表)
    ↓
返回注册结果

代码位置详解

1. 注册接口(Controller层)

文件位置: book/src/main/java/com/zhentao/controller/UserAuthController.java

方法: register() - 第45-52行

@PostMapping("/register")
public Result<String> register(@RequestBody User user) {
    user.setUserRole(0); // 普通用户注册
    if (userService.register(user)) {
        return Result.success("注册成功");
    }
    return Result.error("用户名已存在");
}

接口地址: POST /api/user/register

请求示例:

{
  "username": "testuser",
  "password": "123456",
  "nickname": "测试用户",
  "phone": "13800000000",
  "email": "test@example.com"
}

2. 注册业务逻辑(Service层)

文件位置: book/src/main/java/com/zhentao/service/impl/UserServiceImpl.java

方法: register() - 第47-67行

@Override
public boolean register(User user) {
    // 1. 检查用户名是否已存在
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getUsername, user.getUsername());
    if (count(wrapper) > 0) {
        return false; // 用户名已存在
    }
    
    // 2. 【关键】使用BCrypt加密密码
    user.setPassword(passwordEncoder.encode(user.getPassword()));
    
    // 3. 设置默认值
    if (user.getUserRole() == null) {
        user.setUserRole(0); // 默认普通用户
    }
    if (user.getMemberLevel() == null) {
        user.setMemberLevel(0); // 默认普通会员
    }
    if (user.getStatus() == null) {
        user.setStatus(1); // 默认正常状态
    }
    
    // 4. 保存到数据库
    return save(user);
}

关键代码: 第53行 - user.setPassword(passwordEncoder.encode(user.getPassword()));

3. Service接口定义

文件位置: book/src/main/java/com/zhentao/service/UserService.java

方法: register(User user) - 第9行

4. 数据访问层(Mapper)

文件位置: book/src/main/java/com/zhentao/mapper/UserMapper.java

使用MyBatis-Plus,无需手写SQL,自动实现CRUD操作。


用户登录流程

完整流程图

用户提交登录信息
    ↓
Controller接收请求 (UserAuthController.login)
    ↓
Service验证用户名和密码 (UserServiceImpl.login)
    ↓
检查密码格式(BCrypt或明文)
    ↓
使用BCrypt验证密码
    ↓
检查用户状态和角色
    ↓
生成JWT Token (JwtUtil.generateToken)
    ↓
返回Token和用户信息

代码位置详解

1. 普通用户登录接口

文件位置: book/src/main/java/com/zhentao/controller/UserAuthController.java

方法: login() - 第22-43行

@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody Map<String, String> params) {
    String username = params.get("username");
    String password = params.get("password");
    
    // 1. 验证用户名和密码
    User user = userService.login(username, password);
    if (user == null) {
        return Result.error("用户名或密码错误");
    }
    
    // 2. 禁止管理员通过小程序端登录
    if (user.getUserRole() != null && user.getUserRole() == 1) {
        return Result.error(403, "管理员账号请使用Web端管理后台登录");
    }
    
    // 3. 检查账号状态
    if (user.getStatus() != null && user.getStatus() != 1) {
        return Result.error("账号已被禁用");
    }
    
    // 4. 生成JWT Token
    String token = jwtUtil.generateToken(user.getUserId(), user.getUsername(), user.getUserRole());
    
    // 5. 返回结果
    Map<String, Object> data = new HashMap<>();
    data.put("token", token);
    data.put("user", user);
    return Result.success(data);
}

接口地址: POST /api/user/login

请求示例:

{
  "username": "testuser",
  "password": "123456"
}

2. 管理员登录接口

文件位置: book/src/main/java/com/zhentao/controller/AdminController.java

方法: login() - 第20-39行

@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody Map<String, String> params, HttpServletRequest request) {
    String username = params.get("username");
    String password = params.get("password");
    User user = userService.login(username, password);
    if (user == null) {
        return Result.error("用户名或密码错误");
    }
    // 只允许管理员登录
    if (user.getUserRole() != 1 && user.getUserRole() != 2) {
        return Result.error("无管理员权限");
    }
    if (user.getStatus() != 1) {
        return Result.error("账号已被禁用");
    }
    // 使用Session存储(Web端)
    AdminSessionUtil.setLoginUser(request, user);
    Map<String, Object> data = new HashMap<>(1);
    data.put("user", user);
    return Result.success(data);
}

接口地址: POST /api/admin/login

3. 登录验证逻辑(Service层)

文件位置: book/src/main/java/com/zhentao/service/impl/UserServiceImpl.java

方法: login() - 第20-44行

@Override
public User login(String username, String password) {
    // 1. 根据用户名查询用户
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getUsername, username);
    User user = getOne(wrapper);
    if (user == null) {
        return null; // 用户不存在
    }
    
    // 2. 检查密码格式
    String storedPassword = user.getPassword();
    if (storedPassword != null && storedPassword.startsWith("$2")) {
        // BCrypt格式,使用matches验证
        if (passwordEncoder.matches(password, storedPassword)) {
            return user; // 密码正确
        }
    } else {
        // 明文密码(兼容旧数据),直接比较
        if (password.equals(storedPassword)) {
            // 自动升级为BCrypt加密
            user.setPassword(passwordEncoder.encode(password));
            updateById(user);
            return user;
        }
    }
    return null; // 密码错误
}

关键代码:

  • 第29行:检查是否为BCrypt格式
  • 第31行:使用 passwordEncoder.matches() 验证密码
  • 第38行:自动升级明文密码为BCrypt加密

JWT Token机制

1. JWT工具类

文件位置: book/src/main/java/com/zhentao/common/JwtUtil.java

主要方法:

方法 说明 行号
generateToken() 生成JWT Token 第46-52行
getClaimsFromToken() 解析Token获取Claims 第65-71行
getUserIdFromToken() 从Token中获取用户ID 第73-76行
validateToken() 验证Token是否有效 第78-85行

配置位置: book/src/main/resources/application.yml

jwt:
  secret: zhentao-book-secret-key-2024-this-is-a-very-long-secret-key-for-jwt-encryption-which-must-be-at-least-64-characters-long
  expiration: 86400000  # 24小时(毫秒)

2. Token生成流程

代码位置: book/src/main/java/com/zhentao/common/JwtUtil.java 第46-52行

public String generateToken(Integer userId, String username, Integer userRole) {
    Map<String, Object> claims = new HashMap<>();
    claims.put("userId", userId);
    claims.put("username", username);
    claims.put("userRole", userRole);
    return createToken(claims);
}

Token包含的信息:

  • userId - 用户ID
  • username - 用户名
  • userRole - 用户角色
  • iat - 签发时间(自动添加)
  • exp - 过期时间(自动添加)

3. Token使用场景

场景1:小程序端获取用户信息

文件位置: book/src/main/java/com/zhentao/controller/app/AppUserController.java

方法: getCurrentUserInfo() - 第26-47行

@GetMapping("/info")
public Result<User> getCurrentUserInfo(@RequestHeader(value = "Authorization", required = false) String token) {
    // 1. 检查Token格式
    if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
        return Result.error(401, "未登录");
    }
    try {
        // 2. 提取JWT Token
        String jwt = token.substring(7);
        
        // 3. 从Token中获取用户ID
        Integer userId = jwtUtil.getUserIdFromToken(jwt);
        if (userId == null) {
            return Result.error(401, "Token无效");
        }
        
        // 4. 查询用户信息
        User user = userService.getById(userId);
        if (user == null) {
            return Result.error(404, "用户不存在");
        }
        
        // 5. 清除敏感信息
        user.setPassword(null);
        return Result.success(user);
    } catch (Exception e) {
        return Result.error(401, "Token解析失败");
    }
}

请求头格式: Authorization: Bearer <token>


代码文件位置汇总

核心文件

功能 文件路径 说明
实体类 book/src/main/java/com/zhentao/pojo/User.java 用户实体,对应user表
Mapper接口 book/src/main/java/com/zhentao/mapper/UserMapper.java 数据访问层
Service接口 book/src/main/java/com/zhentao/service/UserService.java 业务逻辑接口
Service实现 book/src/main/java/com/zhentao/service/impl/UserServiceImpl.java 核心业务逻辑(注册、登录、密码加密)
普通用户Controller book/src/main/java/com/zhentao/controller/UserAuthController.java 用户注册、登录接口
管理员Controller book/src/main/java/com/zhentao/controller/AdminController.java 管理员登录接口
应用端Controller book/src/main/java/com/zhentao/controller/app/AppUserController.java 小程序端用户信息接口
密码加密配置 book/src/main/java/com/zhentao/config/SecurityConfig.java BCrypt编码器配置
JWT工具类 book/src/main/java/com/zhentao/common/JwtUtil.java JWT Token生成和验证
配置文件 book/src/main/resources/application.yml JWT密钥和过期时间配置

辅助工具

文件路径 说明
book/src/main/java/com/zhentao/util/PasswordGenerator.java 密码加密工具类(用于生成BCrypt密码)
book/src/main/java/com/zhentao/controller/PasswordController.java 密码加密API(管理员工具)

前端相关

文件路径 说明
xiao/api/user.js 小程序端用户API封装
xiao/pages/login/login.js 小程序登录页面
xiao/pages/register/register.js 小程序注册页面
xiao/utils/api.js API请求工具(包含Token处理)

安全机制总结

1. 密码安全

  • BCrypt加密存储:密码以哈希形式存储,无法逆向
  • 自动加盐:每次加密结果不同,防止彩虹表攻击
  • 自动升级:旧数据明文密码自动升级为BCrypt

2. 身份认证

  • JWT Token:无状态认证,适合分布式系统
  • Token过期机制:默认24小时过期
  • 角色分离:管理员和普通用户使用不同登录接口

3. 权限控制

  • 角色验证:登录时检查用户角色
  • 状态检查:禁用账号无法登录
  • 接口隔离:管理员和普通用户接口分离

常见问题

Q1: 如何重置密码?

方法1: 使用密码加密工具API

GET /api/admin/password/encode?password=新密码

(需要管理员登录且完成人脸验证)

方法2: 使用PasswordGenerator工具类生成BCrypt密码,然后执行SQL更新

Q2: 如何查看密码加密后的值?

查看数据库 user 表的 password 字段,BCrypt加密后的密码以 $2a$10$ 开头。

Q3: 为什么登录失败?

可能原因:

  1. 用户名或密码错误
  2. 密码未加密(旧数据),需要升级
  3. 账号被禁用(status = 0)
  4. 管理员通过小程序端登录(被禁止)

Q4: Token过期怎么办?

重新登录获取新的Token。Token默认24小时过期,可在 application.yml 中修改 jwt.expiration 配置。


数据库操作示例

查看用户信息

SELECT user_id, username, password, user_role, status, create_time 
FROM user 
WHERE username = 'testuser';

更新密码(BCrypt加密后)

UPDATE user 
SET password = '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy' 
WHERE username = 'testuser';

启用/禁用用户

-- 禁用用户
UPDATE user SET status = 0 WHERE user_id = 1;

-- 启用用户
UPDATE user SET status = 1 WHERE user_id = 1;

文档版本: v1.0
最后更新: 2024年