feat: user system first complete

This commit is contained in:
2026-02-13 22:52:53 +08:00
parent d1be97a366
commit 7c35cd11ac
11 changed files with 270 additions and 768 deletions

View File

@@ -1,10 +1,42 @@
package icu.sunway.ai_spring_example.Controller.AuthController;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import icu.sunway.ai_spring_example.Controller.AuthController.vo.LoginByEmailPasswordReq;
import icu.sunway.ai_spring_example.Controller.AuthController.vo.RegisterByEmailReq;
import icu.sunway.ai_spring_example.Exception.BusinessException;
import icu.sunway.ai_spring_example.Service.AuthService;
import icu.sunway.ai_spring_example.Utils.ResponseUtils;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
@Resource
private AuthService authService;
@PostMapping("/login")
public Object loginByEmailPassword(@RequestBody LoginByEmailPasswordReq req) {
try {
String token = authService.loginByEmailPassword(req.getEmail(), req.getPassword());
return ResponseUtils.success(token);
} catch (BusinessException e) {
return ResponseUtils.fail(e.getMessage());
}
}
@PostMapping("/register")
public Object registerByEmail(@RequestBody RegisterByEmailReq req) {
try {
authService.registerByEmail(req.getEmail(), req.getPassword(), req.getVerifyCode());
return ResponseUtils.success();
} catch (BusinessException e) {
return ResponseUtils.fail(e.getMessage());
}
}
}

View File

@@ -1,422 +0,0 @@
# Auth模块接口文档
## 文档说明
本文档为Auth认证授权模块的接口详情遵循《接口开发通用规范文档》的所有约定基础路径为`/api/v1/auth`数据交互格式为JSON字符编码UTF-8时间戳单位为毫秒。
## 1. 登录接口
### 接口基本信息
- 接口名称:用户登录
- 接口路径:`/api/v1/auth/login`
- 请求方法POST
- 接口描述:用户通过账号(手机号/用户名)+密码/验证码完成登录返回登录令牌token
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ---------------- |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ---------- | ------- | ------ | ------------------------------------------------ | ------------- |
| account | String | 是 | 登录账号(手机号/用户名) | "13800138000" |
| password | String | 否 | 登录密码与verifyCode二选一 | "Abc123456" |
| verifyCode | String | 否 | 验证码与password二选一 | "8888" |
| isRemember | Boolean | 否 | 是否记住登录默认false记住则token有效期延长 | true |
### 响应示例(成功)
```json
{
"code": 200,
"message": "登录成功",
"timestamp": 1744238900000,
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJleHAiOjE3NDQ4NDM3MDB9.QP8kXjZ8e7R5t5s7k9L2m1n8b7v6c5x4s3a2d1f0g9h8j7k6l5p4o3i2u1y0t9s8r7e6w5q4a3s2d1f0g",
"expireTime": 1744843700000,
"userInfo": {
"userId": 1,
"userName": "admin",
"phoneNumber": "138****8000",
"role": "ADMIN",
"isEnabled": true,
"createTime": 1743238900000
}
}
}
```
### 响应示例(失败)
```json
{
"code": 400,
"message": "验证码错误或已过期",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | -------------------------------- |
| 200 | 登录成功 |
| 400 | 参数错误/验证码错误/账号密码错误 |
| 403 | 账号被封禁 |
| 429 | 登录失败次数过多,账号临时锁定 |
| 500 | 服务器内部错误 |
## 2. 退出登录接口
### 接口基本信息
- 接口名称:用户退出登录
- 接口路径:`/api/v1/auth/logout`
- 请求方法DELETE
- 接口描述:销毁当前用户的登录令牌,退出登录状态
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数
无业务请求参数仅需请求头携带token
### 响应示例(成功)
```json
{
"code": 200,
"message": "退出登录成功",
"timestamp": 1744238900000,
"data": null
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "Token已过期请重新登录",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ---------------- |
| 200 | 退出登录成功 |
| 401 | Token失效/未登录 |
| 500 | 服务器内部错误 |
## 3. 刷新Token接口
### 接口基本信息
- 接口名称:刷新登录令牌
- 接口路径:`/api/v1/auth/refresh-token`
- 请求方法POST
- 接口描述通过旧Token刷新获取新的登录令牌延长登录有效期
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ------ | ------ | ------ | ------------ | ------------------------- |
| token | String | 是 | 旧的登录令牌 | "eyJhbGciOiJIUzI1NiJ9..." |
### 响应示例(成功)
```json
{
"code": 200,
"message": "Token刷新成功",
"timestamp": 1744238900000,
"data": {
"newToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJleHAiOjE3NDU0NDg1MDB9.Z7x6y5w4v3u2t1s0r9e8w7q6a5z4s3d2f1g0h9j8k7l6p5o4i3u2y1t0s9r8e7w6q5a4s3d2f1g0h9j8k7l6p5o4i",
"expireTime": 1745448500000
}
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "Token已失效无法刷新请重新登录",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ------------------ |
| 200 | Token刷新成功 |
| 400 | 旧Token格式错误 |
| 401 | 旧Token已过期/无效 |
| 500 | 服务器内部错误 |
## 4. 获取当前用户信息接口
### 接口基本信息
- 接口名称:获取当前登录用户信息
- 接口路径:`/api/v1/auth/user/info`
- 请求方法GET
- 接口描述:根据登录令牌获取当前用户的基础信息(脱敏展示敏感字段)
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数
无业务请求参数仅需请求头携带token
### 响应示例(成功)
```json
{
"code": 200,
"message": "查询用户信息成功",
"timestamp": 1744238900000,
"data": {
"userId": 1,
"userName": "admin",
"phoneNumber": "138****8000",
"idCard": "310**********1234",
"roleList": ["ADMIN"],
"isEnabled": true,
"createTime": 1743238900000,
"createTimeStr": "2025-01-01 10:00:00",
"lastLoginTime": 1744152500000,
"lastLoginTimeStr": "2025-04-10 08:00:00"
}
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "未登录,请先登录",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ---------------- |
| 200 | 查询用户信息成功 |
| 401 | Token失效/未登录 |
| 404 | 用户信息不存在 |
| 500 | 服务器内部错误 |
## 5. 发送登录验证码接口
### 接口基本信息
- 接口名称:发送登录验证码
- 接口路径:`/api/v1/auth/send-login-code`
- 请求方法POST
- 接口描述:向指定手机号发送登录验证码,限制发送频率
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ---------------- |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ----------- | ------ | ------ | ------------------ | ------------- |
| phoneNumber | String | 是 | 接收验证码的手机号 | "13800138000" |
### 响应示例(成功)
```json
{
"code": 200,
"message": "验证码发送成功,请注意查收",
"timestamp": 1744238900000,
"data": {
"expireTime": 1744239200000 // 验证码过期时间戳5分钟有效期
}
}
```
### 响应示例(失败)
```json
{
"code": 429,
"message": "验证码发送过于频繁请1分钟后重试",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ------------------------------ |
| 200 | 验证码发送成功 |
| 400 | 手机号格式错误 |
| 429 | 发送频率超限 |
| 404 | 手机号未注册 |
| 500 | 验证码发送失败(短信服务异常) |
## 6. 重置密码接口
### 接口基本信息
- 接口名称:重置登录密码
- 接口路径:`/api/v1/auth/reset-password`
- 请求方法PUT
- 接口描述:用户通过手机号+验证码重置登录密码(全量更新密码)
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ---------------- |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ----------- | ------ | ------ | ----------------------------------------------- | ------------- |
| phoneNumber | String | 是 | 绑定的手机号 | "13800138000" |
| verifyCode | String | 是 | 验证码 | "8888" |
| newPassword | String | 是 | 新密码需符合密码规则8-20位含大小写+数字) | "Abc123456" |
### 响应示例(成功)
```json
{
"code": 200,
"message": "密码重置成功,请重新登录",
"timestamp": 1744238900000,
"data": null
}
```
### 响应示例(失败)
```json
{
"code": 400,
"message": "新密码格式不符合要求需包含大小写字母和数字长度8-20位",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | -------------------------------- |
| 200 | 密码重置成功 |
| 400 | 参数错误/验证码错误/密码格式错误 |
| 429 | 验证码发送频率超限/验证次数过多 |
| 404 | 手机号未注册 |
| 500 | 服务器内部错误 |
## 7. 校验Token有效性接口
### 接口基本信息
- 接口名称校验Token有效性
- 接口路径:`/api/v1/auth/verify-token`
- 请求方法GET
- 接口描述:校验当前登录令牌是否有效,返回令牌基础信息
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数
无业务请求参数仅需请求头携带token
### 响应示例(成功)
```json
{
"code": 200,
"message": "Token有效",
"timestamp": 1744238900000,
"data": {
"userId": 1,
"token": "eyJhbGciOiJIUzI1NiJ9...",
"isValid": true,
"expireTime": 1744843700000
}
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "Token无效或已过期",
"timestamp": 1744238900000,
"data": {
"isValid": false
}
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ---------------- |
| 200 | Token有效 |
| 401 | Token无效/已过期 |
| 500 | 服务器内部错误 |
## 通用说明
1. 所有接口响应均遵循通用响应格式,包含`code``message``timestamp`字段,`data`字段按需返回;
2. 敏感信息(手机号、身份证号)均做脱敏处理,符合安全规范;
3. 接口频率限制遵循通用规范验证码接口1分钟/次、1小时/5次登录接口5分钟连续失败5次锁定15分钟通用接口QPS≤10
4. 所有入参均做合法性校验参数错误返回400状态码并提示具体错误信息
5. Token格式为`Bearer + 空格 + token`,需登录的接口未传/传错Token均返回401状态码。

View File

@@ -0,0 +1,9 @@
package icu.sunway.ai_spring_example.Controller.AuthController.vo;
import lombok.Data;
@Data
public class LoginByEmailPasswordReq {
private String email;
private String password;
}

View File

@@ -0,0 +1,10 @@
package icu.sunway.ai_spring_example.Controller.AuthController.vo;
import lombok.Data;
@Data
public class RegisterByEmailReq {
private String email;
private String password;
private String verifyCode;
}

View File

@@ -1,276 +0,0 @@
-- Example Data for User System
-- Roles
INSERT INTO
`role` (
`name`,
`code`,
`description`,
`status`
)
VALUES (
'Super Admin',
'SUPER_ADMIN',
'Full system access',
1
),
(
'Admin',
'ADMIN',
'Administrative access',
1
),
(
'User',
'USER',
'Regular user access',
1
),
(
'Guest',
'GUEST',
'Limited guest access',
1
);
-- Permissions
INSERT INTO
`permission` (
`name`,
`code`,
`description`,
`parent_id`,
`type`,
`path`,
`sort`
)
VALUES
-- Menu permissions
(
'Dashboard',
'dashboard',
'Dashboard menu',
0,
1,
'/dashboard',
1
),
(
'User Management',
'user:manage',
'User management menu',
0,
1,
'/user',
2
),
(
'Role Management',
'role:manage',
'Role management menu',
0,
1,
'/role',
3
),
(
'System Settings',
'system:settings',
'System settings menu',
0,
1,
'/settings',
4
),
-- User management buttons/actions
(
'View Users',
'user:view',
'View user list',
2,
2,
NULL,
1
),
(
'Create User',
'user:create',
'Create new user',
2,
2,
NULL,
2
),
(
'Edit User',
'user:edit',
'Edit user info',
2,
2,
NULL,
3
),
(
'Delete User',
'user:delete',
'Delete user',
2,
2,
NULL,
4
),
-- Role management buttons/actions
(
'View Roles',
'role:view',
'View role list',
3,
2,
NULL,
1
),
(
'Create Role',
'role:create',
'Create new role',
3,
2,
NULL,
2
),
(
'Edit Role',
'role:edit',
'Edit role info',
3,
2,
NULL,
3
),
(
'Delete Role',
'role:delete',
'Delete role',
3,
2,
NULL,
4
),
-- API permissions
(
'User API',
'api:user',
'User API access',
0,
3,
'/api/user/**',
1
),
(
'Role API',
'api:role',
'Role API access',
0,
3,
'/api/role/**',
2
);
-- Users (passwords are bcrypt hashed for 'password123')
INSERT INTO
`user` (
`username`,
`password`,
`email`,
`phone`,
`avatar`,
`status`
)
VALUES (
'admin',
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi',
'admin@example.com',
'13800000001',
NULL,
1
),
(
'john',
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi',
'john@example.com',
'13800000002',
NULL,
1
),
(
'jane',
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi',
'jane@example.com',
'13800000003',
NULL,
1
),
(
'guest',
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi',
'guest@example.com',
'13800000004',
NULL,
1
);
-- User-Role assignments
INSERT INTO
`user_role` (`user_id`, `role_id`)
VALUES (1, 1), -- admin -> SUPER_ADMIN
(2, 2), -- john -> ADMIN
(3, 3), -- jane -> USER
(4, 4);
-- guest -> GUEST
-- Role-Permission assignments
-- SUPER_ADMIN gets all permissions
INSERT INTO
`role_permission` (`role_id`, `permission_id`)
VALUES (1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(1, 7),
(1, 8),
(1, 9),
(1, 10),
(1, 11),
(1, 12),
(1, 13),
(1, 14);
-- ADMIN gets user and role management
INSERT INTO
`role_permission` (`role_id`, `permission_id`)
VALUES (2, 1),
(2, 2),
(2, 3),
(2, 5),
(2, 6),
(2, 7),
(2, 9),
(2, 10),
(2, 11),
(2, 13),
(2, 14);
-- USER gets dashboard and view permissions
INSERT INTO
`role_permission` (`role_id`, `permission_id`)
VALUES (3, 1),
(3, 5),
(3, 9),
(3, 13);
-- GUEST gets only dashboard
INSERT INTO
`role_permission` (`role_id`, `permission_id`)
VALUES (4, 1);

View File

@@ -1,70 +0,0 @@
-- User System Tables
-- User table
CREATE TABLE `user` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`email` VARCHAR(100),
`phone` VARCHAR(20),
`avatar` VARCHAR(255),
`status` INT DEFAULT 1 COMMENT '0: disabled, 1: enabled',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_login_time` DATETIME,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`),
UNIQUE KEY `uk_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Role table
CREATE TABLE `role` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`code` VARCHAR(50) NOT NULL,
`description` VARCHAR(255),
`status` INT DEFAULT 1 COMMENT '0: disabled, 1: enabled',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Permission table
CREATE TABLE `permission` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`code` VARCHAR(100) NOT NULL,
`description` VARCHAR(255),
`parent_id` BIGINT DEFAULT 0,
`type` INT NOT NULL COMMENT '1: menu, 2: button, 3: api',
`path` VARCHAR(255),
`sort` INT DEFAULT 0,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- User-Role junction table
CREATE TABLE `user_role` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`role_id` BIGINT NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_role` (`user_id`, `role_id`),
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`role_id`) REFERENCES `role`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Role-Permission junction table
CREATE TABLE `role_permission` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`role_id` BIGINT NOT NULL,
`permission_id` BIGINT NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_permission` (`role_id`, `permission_id`),
FOREIGN KEY (`role_id`) REFERENCES `role`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`permission_id`) REFERENCES `permission`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,32 @@
package icu.sunway.ai_spring_example.Exception;
import lombok.Getter;
/**
* 自定义业务异常类
*/
@Getter
public class BusinessException extends RuntimeException {
/**
* 业务错误码(区分不同异常场景)
*/
private int code;
// 仅错误消息
public BusinessException(String message) {
super(message);
this.code = 500; // 默认业务异常码
}
// 错误码 + 错误消息
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
// 错误码 + 错误消息 + 根因异常
public BusinessException(int code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}

View File

@@ -0,0 +1,66 @@
package icu.sunway.ai_spring_example.Service;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import icu.sunway.ai_spring_example.Entity.User;
import icu.sunway.ai_spring_example.Exception.BusinessException;
import icu.sunway.ai_spring_example.Service.Implements.UserServiceImpl;
import icu.sunway.ai_spring_example.Utils.JwtUtils;
import icu.sunway.ai_spring_example.Utils.RedisUtils;
import jakarta.annotation.Resource;
@Service
public class AuthService {
@Resource
private UserServiceImpl userService;
@Resource
private MailService mailService;
@Resource
private RedisUtils redisUtils;
public void registerByEmail(String email, String password, String verifyCode) {
// 校验邮箱是否已存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("email", email);
if (userService.getOne(queryWrapper) != null) {
throw new BusinessException("邮箱已存在");
}
// 校验验证码正确性
if (!mailService.checkVerifyCode(email, verifyCode)) {
throw new BusinessException("验证码错误");
}
User user = new User();
user.setEmail(email);
// 用户名是 not null暂时设置为邮箱
user.setUsername(email);
user.setPassword(password);
userService.save(user);
}
public String loginByEmailPassword(String email, String password) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("email", email);
// 校验邮箱是否存在
if (userService.getOne(queryWrapper) == null) {
throw new BusinessException("邮箱不存在");
}
queryWrapper.eq("password", password);
User user = userService.getOne(queryWrapper);
if (user == null) {
throw new BusinessException("邮箱或密码错误");
}
String token = JwtUtils.generateToken(user.getId().toString());
// 存放 token 到 Redis
redisUtils.set("token:" + token, token, JwtUtils.getTime(), TimeUnit.MILLISECONDS);
return token;
}
public boolean checkToken(String token) {
return redisUtils.hasKey("token:" + token);
}
}

View File

@@ -0,0 +1,24 @@
package icu.sunway.ai_spring_example.Service;
import org.springframework.stereotype.Service;
import icu.sunway.ai_spring_example.Utils.RedisUtils;
import jakarta.annotation.Resource;
@Service
public class MailService {
@Resource
private RedisUtils redisUtils;
public Boolean checkVerifyCode(String to, String toBeCheckedCode) {
// if (redisUtils.hasKey("email:verify:" + to)) {
// String verifyCode = redisUtils.get("email:verify:" + to).toString();
// return verifyCode.equals(toBeCheckedCode);
// }
//
// return false;
// 暂时没有发邮件,默认全部通过
return true;
}
}

View File

@@ -0,0 +1,51 @@
package icu.sunway.ai_spring_example.Utils;
import java.util.Date;
import java.util.UUID;
import javax.crypto.SecretKey;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.Getter;
public class JwtUtils {
@Getter
private static final long time = 1000 * 3600 * 24 * 7; // 7天过期
private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
public static String generateToken(String id) {
JwtBuilder jwtBuilder = Jwts.builder();
return jwtBuilder
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.claim("id", id)
.claim("date", new Date())
.setExpiration(new Date(System.currentTimeMillis() + time))
.signWith(SECRET_KEY, SignatureAlgorithm.HS256)
.setId(UUID.randomUUID().toString())
.setSubject("user-check")
.compact();
}
public static String parseTokenAsId(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody();
try {
if (claims.getExpiration().before(new Date())) {
return null;
}
return claims.get("id", String.class);
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,46 @@
package icu.sunway.ai_spring_example.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogUtils {
private static final Logger logger = LoggerFactory.getLogger(LogUtils.class);
public static void info(String message) {
logger.info("------------ debug info start -----------");
logger.info(message);
logger.info("------------ debug info end -----------");
}
public static void error(String message) {
logger.error(message);
}
public static void error(String message, Throwable throwable) {
logger.error(message, throwable);
}
public static void debug(String message) {
logger.debug(message);
}
public static void debug(String message, Throwable throwable) {
logger.debug(message, throwable);
}
public static void trace(String message) {
logger.trace(message);
}
public static void trace(String message, Throwable throwable) {
logger.trace(message, throwable);
}
public static void warn(String message) {
logger.warn(message);
}
public static void warn(String message, Throwable throwable) {
logger.warn(message, throwable);
}
}