表名: 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
为什么使用BCrypt?
配置文件位置: book/src/main/java/com/zhentao/config/SecurityConfig.java
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
关键代码: 第16-18行
加密后的密码格式:$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
$2a$ - BCrypt版本标识10 - 加密强度(cost factor)用户提交注册信息
↓
Controller接收请求 (UserAuthController.register)
↓
检查用户名是否已存在
↓
使用BCrypt加密密码 (UserServiceImpl.register)
↓
设置默认值(角色、会员等级、状态)
↓
保存到数据库 (user表)
↓
返回注册结果
文件位置: 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"
}
文件位置: 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()));
文件位置: book/src/main/java/com/zhentao/service/UserService.java
方法: register(User user) - 第9行
文件位置: 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和用户信息
文件位置: 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"
}
文件位置: 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
文件位置: 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; // 密码错误
}
关键代码:
passwordEncoder.matches() 验证密码文件位置: 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小时(毫秒)
代码位置: 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 - 用户IDusername - 用户名userRole - 用户角色iat - 签发时间(自动添加)exp - 过期时间(自动添加)文件位置: 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: 使用密码加密工具API
GET /api/admin/password/encode?password=新密码
(需要管理员登录且完成人脸验证)
方法2: 使用PasswordGenerator工具类生成BCrypt密码,然后执行SQL更新
查看数据库 user 表的 password 字段,BCrypt加密后的密码以 $2a$10$ 开头。
可能原因:
重新登录获取新的Token。Token默认24小时过期,可在 application.yml 中修改 jwt.expiration 配置。
SELECT user_id, username, password, user_role, status, create_time
FROM user
WHERE username = 'testuser';
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年