diff --git a/.idea/.cache/.Apifox_Helper/.toolWindow.db b/.idea/.cache/.Apifox_Helper/.toolWindow.db
new file mode 100644
index 0000000..a5693ff
Binary files /dev/null and b/.idea/.cache/.Apifox_Helper/.toolWindow.db differ
diff --git a/.idea/ApifoxUploaderProjectSetting.xml b/.idea/ApifoxUploaderProjectSetting.xml
index fc0729c..4c2b765 100644
--- a/.idea/ApifoxUploaderProjectSetting.xml
+++ b/.idea/ApifoxUploaderProjectSetting.xml
@@ -2,5 +2,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/fastRequest/fastRequestCollection.xml b/.idea/fastRequest/fastRequestCollection.xml
new file mode 100644
index 0000000..a433492
--- /dev/null
+++ b/.idea/fastRequest/fastRequestCollection.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/fastRequest/fastRequestCurrentProjectLocalConfig.xml b/.idea/fastRequest/fastRequestCurrentProjectLocalConfig.xml
index e62f560..e03c3b9 100644
--- a/.idea/fastRequest/fastRequestCurrentProjectLocalConfig.xml
+++ b/.idea/fastRequest/fastRequestCurrentProjectLocalConfig.xml
@@ -1,6 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/docs/video-auth模块目录架构、功能及接口文档.md b/docs/video-auth模块目录架构、功能及接口文档.md
new file mode 100644
index 0000000..9f59a0d
--- /dev/null
+++ b/docs/video-auth模块目录架构、功能及接口文档.md
@@ -0,0 +1,269 @@
+# video-auth模块目录架构、功能及接口文档
+
+## 一、目录架构
+
+```
+video-auth/
+├── src/
+│ ├── main/
+│ │ ├── java/
+│ │ │ └── com/nuaa/video/auth/
+│ │ │ ├── config/ # 配置类
+| | | │ ├── RedisConfig.java # Redis配置
+│ │ │ │ └── SecurityConfig.java # 安全配置
+│ │ │ ├── controller/ # 控制器
+│ │ │ │ └── AuthController.java # 认证控制器
+│ │ │ ├── dto/ # 数据传输对象
+│ │ │ │ ├── LoginDTO.java # 登录请求DTO
+│ │ │ │ ├── RegisterDTO.java # 注册请求DTO
+│ │ │ │ └── TokenInfoDTO.java # 令牌信息DTO
+│ │ │ ├── entity/ # 实体类
+│ │ │ │ ├── AuthUser.java # 用户实体
+│ │ │ │ ├── AuthRole.java # 角色实体
+│ │ │ │ ├── AuthPermission.java # 权限实体
+│ │ │ │ ├── AuthUserRole.java # 用户角色关联实体
+│ │ │ │ └── AuthRolePermission.java # 角色权限关联实体
+│ │ │ ├── filter/ # 过滤器
+│ │ │ │ └── JwtAuthenticationFilter.java # JWT认证过滤器
+│ │ │ ├── mapper/ # MyBatis映射接口
+│ │ │ │ ├── AuthUserMapper.java # 用户Mapper
+│ │ │ │ ├── AuthRoleMapper.java # 角色Mapper
+│ │ │ │ ├── AuthPermissionMapper.java # 权限Mapper
+│ │ │ │ ├── AuthUserRoleMapper.java # 用户角色Mapper
+│ │ │ │ └── AuthRolePermissionMapper.java # 角色权限Mapper
+│ │ │ ├── service/ # 服务层
+│ │ │ │ ├── AuthUserService.java # 用户服务接口
+│ │ │ │ ├── CaptchaService.java # 验证码服务接口
+│ │ │ │ └── impl/ # 实现类
+│ │ │ │ ├── AuthUserServiceImpl.java
+│ │ │ │ └── CaptchaServiceImpl.java
+│ │ │ ├── utils/ # 工具类
+│ │ │ │ ├── RedisUtils.java # Redis工具类
+│ │ │ │ └── JwtUtils.java # JWT工具类
+│ │ │ └── AuthApplication.java # 启动类
+│ │ └── resources/
+│ │ ├── mapper/ # MyBatis XML映射文件
+│ │ │ └── AuthUserMapper.xml
+│ │ ├── application.yml # 配置文件
+│ │ ├── application-dev.yml # 开发环境配置
+└── pom.xml
+```
+
+## 二、功能概述
+
+### 1. 核心功能
+
+- **用户认证**:登录、注册、令牌刷新、登出
+- **安全管理**:JWT生成与验证、密码加密
+- **角色权限**:用户角色和权限的管理
+- **验证码服务**:生成和验证图形验证码
+
+### 2. 业务流程
+
+1. **用户注册**:用户提交注册信息 → 验证码验证 → 创建用户 → 分配默认角色
+2. **用户登录**:用户提交登录信息 → 验证用户凭证 → 生成JWT令牌 → 缓存用户认证信息
+3. **认证鉴权**:请求携带JWT令牌 → JWT过滤器验证令牌 → 提取用户信息 → 设置安全上下文 → 授权访问
+
+## 三、接口文档
+
+### 1. 用户登录
+
+- **URI**: `/auth/login`
+- **方法**: POST
+- **描述**: 用户登录并获取令牌
+- **请求Body**:
+ ```json
+ {
+ "username": "admin",
+ "password": "admin123",
+ "captcha": "1a2b",
+ "captchaKey": "abcd1234"
+ }
+ ```
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "accessToken": "eyJhbGciOiJIUzI1NiJ9...",
+ "refreshToken": "nQs6NGPzAJbfw5ddsSVGY2iqAomOmGW9",
+ "tokenType": "Bearer",
+ "expiresIn": 86400,
+ "userId": 1,
+ "username": "admin"
+ }
+ }
+ ```
+- **错误码**:
+ - `400`: 参数错误(如验证码错误)
+ - `401`: 用户名或密码错误
+ - `403`: 账号已被禁用
+
+### 2. 用户注册
+
+- **URI**: `/auth/register`
+- **方法**: POST
+- **描述**: 注册新用户
+- **请求Body**:
+ ```json
+ {
+ "username": "newuser",
+ "password": "password123",
+ "confirmPassword": "password123",
+ "email": "user@example.com",
+ "phone": "13800138000",
+ "captcha": "1a2b",
+ "captchaKey": "abcd1234"
+ }
+ ```
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": "注册成功"
+ }
+ ```
+- **错误码**:
+ - `400`: 参数错误(如密码不匹配、验证码错误)
+ - `1002`: 用户名已存在
+
+### 3. 获取验证码
+
+- **URI**: `/auth/captcha`
+- **方法**: GET
+- **描述**: 生成图形验证码
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "captchaKey": "abcd1234",
+ "captchaImage": "data:image/png;base64,iVBORw0KGgoAAAANSUhEU..."
+ }
+ }
+ ```
+
+### 4. 刷新令牌
+
+- **URI**: `/auth/refresh`
+- **方法**: POST
+- **描述**: 使用刷新令牌获取新的访问令牌
+- **请求参数**:
+ - `refreshToken`: 刷新令牌字符串
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "accessToken": "eyJhbGciOiJIUzI1NiJ9...",
+ "refreshToken": "new_refresh_token",
+ "tokenType": "Bearer",
+ "expiresIn": 86400,
+ "userId": 1,
+ "username": "admin"
+ }
+ }
+ ```
+- **错误码**:
+ - `401`: 刷新令牌无效或已过期
+
+### 5. 用户登出
+
+- **URI**: `/auth/logout`
+- **方法**: POST
+- **描述**: 用户登出,清除令牌
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+### 6. 获取当前用户信息
+
+- **URI**: `/auth/current`
+- **方法**: GET
+- **描述**: 获取当前登录用户的详细信息
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "id": 1,
+ "username": "admin",
+ "email": "admin@example.com",
+ "phone": "13800138000",
+ "status": 1,
+ "lastLoginTime": "2024-03-30T12:30:45",
+ "lastLoginIp": "192.168.1.1",
+ "roles": ["USER_ADMIN"]
+ }
+ }
+ ```
+- **错误码**:
+ - `401`: 未授权
+ - `404`: 用户不存在
+
+## 四、数据库设计
+
+认证模块涉及的主要数据库表:
+
+### 1. auth_user (用户表)
+- `id`: 用户ID (主键)
+- `username`: 用户名
+- `password`: 加密密码
+- `salt`: 加密盐值
+- `email`: 邮箱
+- `phone`: 手机号
+- `status`: 状态(0:禁用,1:正常)
+- `last_login_time`: 最后登录时间
+- `last_login_ip`: 最后登录IP
+
+### 2. auth_role (角色表)
+- `id`: 角色ID (主键)
+- `name`: 角色名称
+- `code`: 角色编码
+- `description`: 角色描述
+
+### 3. auth_permission (权限表)
+- `id`: 权限ID (主键)
+- `name`: 权限名称
+- `code`: 权限编码
+- `description`: 权限描述
+
+### 4. auth_user_role (用户角色关联表)
+- `id`: 主键
+- `user_id`: 用户ID (外键)
+- `role_id`: 角色ID (外键)
+
+### 5. auth_role_permission (角色权限关联表)
+- `id`: 主键
+- `role_id`: 角色ID (外键)
+- `permission_id`: 权限ID (外键)
+
+## 五、认证流程说明
+
+1. **JWT认证流程**:
+ - 用户登录成功后,服务端生成JWT令牌和刷新令牌
+ - 客户端请求时在请求头中携带JWT令牌
+ - 服务端通过JwtAuthenticationFilter验证令牌
+ - 验证通过后,设置SecurityContext并放行请求
+
+2. **权限校验流程**:
+ - 用户登录时加载其角色和权限
+ - 将权限信息缓存到Redis中
+ - 请求鉴权时从SecurityContext获取用户信息
+ - 根据用户角色和权限决定是否允许访问
+
+video-auth模块提供了完整的用户认证、授权和安全管理功能,为整个视频平台提供统一的身份验证服务。
\ No newline at end of file
diff --git a/video-auth/pom.xml b/video-auth/pom.xml
index 55a08f4..baf9e7e 100644
--- a/video-auth/pom.xml
+++ b/video-auth/pom.xml
@@ -12,12 +12,19 @@
video-auth
+
com.nuaa.video
video-common
${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
org.springframework.boot
@@ -28,7 +35,100 @@
org.springframework.security
spring-security-oauth2-authorization-server
- 1.2.1
+ 1.2.3
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+
+
+ com.baomidou
+ mybatis-plus-spring-boot3-starter
+ 3.5.11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+ cn.hutool
+ hutool-all
+ 5.8.26
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/AuthApplication.java b/video-auth/src/main/java/com/nuaa/video/auth/AuthApplication.java
index 7ba42cd..1f7db2b 100644
--- a/video-auth/src/main/java/com/nuaa/video/auth/AuthApplication.java
+++ b/video-auth/src/main/java/com/nuaa/video/auth/AuthApplication.java
@@ -1,11 +1,14 @@
package com.nuaa.video.auth;
+import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
+@EnableFeignClients
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/config/RedisConfig.java b/video-auth/src/main/java/com/nuaa/video/auth/config/RedisConfig.java
new file mode 100644
index 0000000..3d9f812
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/config/RedisConfig.java
@@ -0,0 +1,38 @@
+package com.nuaa.video.auth.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+
+@Configuration
+public class RedisConfig {
+
+ @Bean
+ public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
+ RedisTemplate template = new RedisTemplate<>();
+ template.setConnectionFactory(factory);
+
+ // 创建配置好的ObjectMapper
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+ mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
+
+ // 使用Jackson2JsonRedisSerializer
+ Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(mapper, Object.class);
+
+ template.setKeySerializer(new StringRedisSerializer());
+ template.setValueSerializer(serializer);
+ template.setHashKeySerializer(new StringRedisSerializer());
+ template.setHashValueSerializer(serializer);
+
+ template.afterPropertiesSet();
+ return template;
+ }
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/config/SecurityConfig.java b/video-auth/src/main/java/com/nuaa/video/auth/config/SecurityConfig.java
new file mode 100644
index 0000000..e6b213d
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/config/SecurityConfig.java
@@ -0,0 +1,85 @@
+package com.nuaa.video.auth.config;
+
+import cn.hutool.jwt.Claims;
+import com.nuaa.video.auth.filter.JwtAuthenticationFilter;
+import com.nuaa.video.auth.service.AuthUserService;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.servlet.View;
+
+import java.io.IOException;
+
+/**
+ * 安全配置
+ */
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+ private final AuthUserService authUserService;
+
+ // 使用构造注入并添加@Lazy注解
+ public SecurityConfig(@Lazy AuthUserService authUserService) {
+ this.authUserService = authUserService;
+ }
+
+ /**
+ * 密码编码器
+ */
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ /**
+ * 认证管理器
+ */
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
+ return config.getAuthenticationManager();
+ }
+
+ /**
+ * 安全过滤器链
+ */
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http, View error, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
+ http
+ .csrf(csrf -> csrf.disable())
+ .cors(cors -> cors.disable())
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers(
+ "/auth/login",
+ "/auth/register",
+ "/auth/captcha",
+ "/actuator/**",
+ "/error"
+ ).permitAll()
+ .anyRequest().authenticated()
+ )
+ //添加JWT认证过滤器
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+
+ return http.build();
+ }
+
+
+}
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/controller/AuthController.java b/video-auth/src/main/java/com/nuaa/video/auth/controller/AuthController.java
new file mode 100644
index 0000000..01998eb
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/controller/AuthController.java
@@ -0,0 +1,116 @@
+package com.nuaa.video.auth.controller;
+
+import com.nuaa.video.auth.dto.LoginDTO;
+import com.nuaa.video.auth.dto.RegisterDTO;
+import com.nuaa.video.auth.dto.TokenInfoDTO;
+import com.nuaa.video.auth.entity.AuthUser;
+import com.nuaa.video.auth.service.AuthUserService;
+import com.nuaa.video.auth.service.CaptchaService;
+import com.nuaa.video.common.model.vo.Result;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * 认证控制器
+ */
+@Slf4j
+@RestController
+@RequestMapping("/auth")
+@RequiredArgsConstructor
+public class AuthController {
+
+ private final AuthUserService authUserService;
+ private final CaptchaService captchaService;
+
+ /**
+ * 用户登录
+ */
+ @PostMapping("/login")
+ public Result login(@RequestBody @Valid LoginDTO loginDTO) {
+ return authUserService.login(loginDTO);
+ }
+
+ /**
+ * 用户注册
+ */
+ @PostMapping("/register")
+ public Result register(@RequestBody @Valid RegisterDTO registerDTO) {
+ return authUserService.register(registerDTO);
+ }
+
+ /**
+ * 生成验证码
+ */
+ @GetMapping("/captcha")
+ public Result> captcha() {
+ return captchaService.generateCaptcha();
+ }
+
+ /**
+ * 刷新令牌
+ */
+ @PostMapping("/refresh")
+ public Result refreshToken(@RequestParam("refreshToken") String refreshToken) {
+ return authUserService.refreshToken(refreshToken);
+ }
+
+ /**
+ * 用户登出
+ */
+ @PostMapping("/logout")
+ public Result logout(HttpServletRequest request) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return authUserService.logout(userId);
+ }
+
+ /**
+ * 获取当前用户信息
+ */
+ @GetMapping("/current")
+ public Result currentUser(HttpServletRequest request) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return authUserService.getCurrentUser(userId);
+ }
+
+ /**
+ * 从请求中获取用户ID
+ */
+ private Long getUserIdFromRequest(HttpServletRequest request) {
+ // 首先尝试从请求属性中获取
+ Object userIdAttr = request.getAttribute("userId");
+ if (userIdAttr != null) {
+ return (Long) userIdAttr;
+ }
+
+ // 如果请求属性中没有,尝试从认证上下文获取
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication != null && authentication.getDetails() instanceof Long) {
+ return (Long) authentication.getDetails();
+ }
+
+ // 最后尝试从请求头获取(如果前端直接传递)
+ String userIdHeader = request.getHeader("userId");
+ if (userIdHeader != null && !userIdHeader.isEmpty()) {
+ try {
+ return Long.parseLong(userIdHeader);
+ } catch (NumberFormatException e) {
+ log.warn("Invalid userId header: {}", userIdHeader);
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/dto/LoginDTO.java b/video-auth/src/main/java/com/nuaa/video/auth/dto/LoginDTO.java
new file mode 100644
index 0000000..6d8c13e
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/dto/LoginDTO.java
@@ -0,0 +1,36 @@
+package com.nuaa.video.auth.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 登录请求DTO
+ */
+@Data
+public class LoginDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户名
+ */
+ @NotBlank(message = "用户名不能为空")
+ private String username;
+
+ /**
+ * 密码
+ */
+ @NotBlank(message = "密码不能为空")
+ private String password;
+
+ /**
+ * 验证码
+ */
+ private String captcha;
+
+ /**
+ * 验证码KEY
+ */
+ private String captchaKey;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/dto/RegisterDTO.java b/video-auth/src/main/java/com/nuaa/video/auth/dto/RegisterDTO.java
new file mode 100644
index 0000000..16c54e5
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/dto/RegisterDTO.java
@@ -0,0 +1,60 @@
+package com.nuaa.video.auth.dto;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 注册请求DTO
+ */
+@Data
+public class RegisterDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户名
+ */
+ @NotBlank(message = "用户名不能为空")
+ @Pattern(regexp = "^[a-zA-Z0-9_]{4,16}$", message = "用户名必须为4-16位字母、数字或下划线")
+ private String username;
+
+ /**
+ * 密码
+ */
+ @NotBlank(message = "密码不能为空")
+ @Pattern(regexp = "^.{6,20}$", message = "密码长度必须为6-20位")
+ private String password;
+
+ /**
+ * 确认密码
+ */
+ @NotBlank(message = "确认密码不能为空")
+ private String confirmPassword;
+
+ /**
+ * 邮箱
+ */
+ @Email(message = "邮箱格式不正确")
+ private String email;
+
+ /**
+ * 手机号
+ */
+ @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
+ private String phone;
+
+ /**
+ * 验证码
+ */
+ @NotBlank(message = "验证码不能为空")
+ private String captcha;
+
+ /**
+ * 验证码KEY
+ */
+ @NotBlank(message = "验证码KEY不能为空")
+ private String captchaKey;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/dto/TokenInfoDTO.java b/video-auth/src/main/java/com/nuaa/video/auth/dto/TokenInfoDTO.java
new file mode 100644
index 0000000..6973003
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/dto/TokenInfoDTO.java
@@ -0,0 +1,45 @@
+package com.nuaa.video.auth.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * Token信息DTO
+ */
+@Data
+@Accessors(chain = true)
+public class TokenInfoDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 访问令牌
+ */
+ private String accessToken;
+
+ /**
+ * 刷新令牌
+ */
+ private String refreshToken;
+
+ /**
+ * 令牌类型
+ */
+ private String tokenType = "Bearer";
+
+ /**
+ * 过期时间(秒)
+ */
+ private long expiresIn;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 用户名
+ */
+ private String username;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthPermission.java b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthPermission.java
new file mode 100644
index 0000000..6d0cd4e
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthPermission.java
@@ -0,0 +1,59 @@
+package com.nuaa.video.auth.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 权限实体
+ */
+@Data
+@TableName("auth_permission")
+public class AuthPermission implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 权限ID
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 权限名称
+ */
+ private String name;
+
+ /**
+ * 权限编码
+ */
+ private String code;
+
+ /**
+ * 权限描述
+ */
+ private String description;
+
+ /**
+ * 创建人
+ */
+ private String createdBy;
+
+ /**
+ * 更新人
+ */
+ private String updatedBy;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthRole.java b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthRole.java
new file mode 100644
index 0000000..66981c8
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthRole.java
@@ -0,0 +1,59 @@
+package com.nuaa.video.auth.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 角色实体
+ */
+@Data
+@TableName("auth_role")
+public class AuthRole implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 角色ID
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 角色名称
+ */
+ private String name;
+
+ /**
+ * 角色编码
+ */
+ private String code;
+
+ /**
+ * 角色描述
+ */
+ private String description;
+
+ /**
+ * 创建人
+ */
+ private String createdBy;
+
+ /**
+ * 更新人
+ */
+ private String updatedBy;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthRolePermission.java b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthRolePermission.java
new file mode 100644
index 0000000..07bb5cd
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthRolePermission.java
@@ -0,0 +1,44 @@
+package com.nuaa.video.auth.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 角色权限关联实体
+ */
+@Data
+@TableName("auth_role_permission")
+public class AuthRolePermission implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键ID
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 角色ID
+ */
+ private Long roleId;
+
+ /**
+ * 权限ID
+ */
+ private Long permissionId;
+
+ /**
+ * 创建人
+ */
+ private String createdBy;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthUser.java b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthUser.java
new file mode 100644
index 0000000..b6c2f68
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthUser.java
@@ -0,0 +1,91 @@
+package com.nuaa.video.auth.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 用户认证实体
+ */
+@Data
+@TableName("auth_user")
+public class AuthUser implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户ID
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户名
+ */
+ private String username;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 加密盐值
+ */
+ private String salt;
+
+ /**
+ * 邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号
+ */
+ private String phone;
+
+ /**
+ * 状态(0:禁用,1:正常)
+ */
+ private Integer status;
+
+ /**
+ * 最后登录时间
+ */
+ private LocalDateTime lastLoginTime;
+
+ /**
+ * 最后登录IP
+ */
+ private String lastLoginIp;
+
+ /**
+ * 创建人
+ */
+ private String createdBy;
+
+ /**
+ * 更新人
+ */
+ private String updatedBy;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 拥有的角色列表(非数据库字段)
+ */
+ @TableField(exist = false)
+ private String[] roles;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthUserRole.java b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthUserRole.java
new file mode 100644
index 0000000..ebd2831
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/entity/AuthUserRole.java
@@ -0,0 +1,44 @@
+package com.nuaa.video.auth.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 用户角色关联实体
+ */
+@Data
+@TableName("auth_user_role")
+public class AuthUserRole implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键ID
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 角色ID
+ */
+ private Long roleId;
+
+ /**
+ * 创建人
+ */
+ private String createdBy;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/filter/JwtAuthenticationFilter.java b/video-auth/src/main/java/com/nuaa/video/auth/filter/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..e5f7edc
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/filter/JwtAuthenticationFilter.java
@@ -0,0 +1,84 @@
+package com.nuaa.video.auth.filter;
+
+import com.nuaa.video.auth.utils.JwtUtils;
+import io.jsonwebtoken.Claims;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JWT认证过滤器
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+ private final JwtUtils jwtUtils;
+ private static final String TOKEN_PREFIX = "Bearer ";
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+ // 从请求头中获取JWT令牌
+ String token = getJwtFromRequest(request);
+
+ // 如果令牌存在且有效,则设置认证信息
+ if (StringUtils.hasText(token)) {
+ try {
+ // 验证令牌并提取用户ID
+ Long userId = jwtUtils.getUserIdFromToken(token);
+ String username = jwtUtils.getUsernameFromToken(token);
+
+ if (userId != null && !jwtUtils.isTokenExpired(token)) {
+ log.debug("JWT验证成功, userId={}, username={}", userId, username);
+
+ // 设置用户权限(这里使用空列表,实际应用中可以从令牌或数据库获取)
+ List authorities = new ArrayList<>();
+
+ // 创建认证对象
+ UsernamePasswordAuthenticationToken authentication =
+ new UsernamePasswordAuthenticationToken(username, null, authorities);
+
+ // 设置额外信息
+ authentication.setDetails(userId);
+
+ // 设置认证信息到安全上下文
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ // 在请求属性中存储用户ID,供控制器使用
+ request.setAttribute("userId", userId);
+ }
+ } catch (Exception e) {
+ log.error("JWT验证失败: {}", e.getMessage());
+ SecurityContextHolder.clearContext();
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ /**
+ * 从请求头中提取JWT令牌
+ */
+ private String getJwtFromRequest(HttpServletRequest request) {
+ String bearerToken = request.getHeader("Authorization");
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
+ return bearerToken.substring(TOKEN_PREFIX.length());
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthPermissionMapper.java b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthPermissionMapper.java
new file mode 100644
index 0000000..4f1b8c6
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthPermissionMapper.java
@@ -0,0 +1,12 @@
+package com.nuaa.video.auth.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.nuaa.video.auth.entity.AuthPermission;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 权限Mapper接口
+ */
+@Mapper
+public interface AuthPermissionMapper extends BaseMapper {
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthRoleMapper.java b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthRoleMapper.java
new file mode 100644
index 0000000..16123ad
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthRoleMapper.java
@@ -0,0 +1,12 @@
+package com.nuaa.video.auth.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.nuaa.video.auth.entity.AuthRole;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 角色Mapper接口
+ */
+@Mapper
+public interface AuthRoleMapper extends BaseMapper {
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthRolePermissionMapper.java b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthRolePermissionMapper.java
new file mode 100644
index 0000000..3a034a2
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthRolePermissionMapper.java
@@ -0,0 +1,12 @@
+package com.nuaa.video.auth.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.nuaa.video.auth.entity.AuthRolePermission;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 角色权限关联Mapper接口
+ */
+@Mapper
+public interface AuthRolePermissionMapper extends BaseMapper {
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthUserMapper.java b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthUserMapper.java
new file mode 100644
index 0000000..f1f3e43
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthUserMapper.java
@@ -0,0 +1,31 @@
+package com.nuaa.video.auth.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.nuaa.video.auth.entity.AuthUser;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 用户Mapper接口
+ */
+@Mapper
+public interface AuthUserMapper extends BaseMapper {
+
+ /**
+ * 获取用户角色
+ *
+ * @param userId 用户ID
+ * @return 角色编码列表
+ */
+ List getUserRoles(@Param("userId") Long userId);
+
+ /**
+ * 获取用户权限
+ *
+ * @param userId 用户ID
+ * @return 权限编码列表
+ */
+ List getUserPermissions(@Param("userId") Long userId);
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthUserRoleMapper.java b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthUserRoleMapper.java
new file mode 100644
index 0000000..1d39c44
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/mapper/AuthUserRoleMapper.java
@@ -0,0 +1,12 @@
+package com.nuaa.video.auth.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.nuaa.video.auth.entity.AuthUserRole;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 用户角色关联Mapper接口
+ */
+@Mapper
+public interface AuthUserRoleMapper extends BaseMapper {
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/service/AuthUserService.java b/video-auth/src/main/java/com/nuaa/video/auth/service/AuthUserService.java
new file mode 100644
index 0000000..84d0f7f
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/service/AuthUserService.java
@@ -0,0 +1,62 @@
+package com.nuaa.video.auth.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.nuaa.video.auth.dto.LoginDTO;
+import com.nuaa.video.auth.dto.RegisterDTO;
+import com.nuaa.video.auth.dto.TokenInfoDTO;
+import com.nuaa.video.auth.entity.AuthUser;
+import com.nuaa.video.common.model.vo.Result;
+
+/**
+ * 用户认证服务接口
+ */
+public interface AuthUserService extends IService {
+
+ /**
+ * 用户登录
+ *
+ * @param loginDTO 登录信息
+ * @return 登录结果
+ */
+ Result login(LoginDTO loginDTO);
+
+ /**
+ * 用户注册
+ *
+ * @param registerDTO 注册信息
+ * @return 注册结果
+ */
+ Result register(RegisterDTO registerDTO);
+
+ /**
+ * 通过用户名查询用户
+ *
+ * @param username 用户名
+ * @return 用户信息
+ */
+ AuthUser getByUsername(String username);
+
+ /**
+ * 刷新令牌
+ *
+ * @param refreshToken 刷新令牌
+ * @return 新的令牌信息
+ */
+ Result refreshToken(String refreshToken);
+
+ /**
+ * 登出
+ *
+ * @param userId 用户ID
+ * @return 登出结果
+ */
+ Result logout(Long userId);
+
+ /**
+ * 获取当前登录用户信息
+ *
+ * @param userId 用户ID
+ * @return 用户信息
+ */
+ Result getCurrentUser(Long userId);
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/service/CaptchaService.java b/video-auth/src/main/java/com/nuaa/video/auth/service/CaptchaService.java
new file mode 100644
index 0000000..11377d4
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/service/CaptchaService.java
@@ -0,0 +1,18 @@
+package com.nuaa.video.auth.service;
+
+import com.nuaa.video.common.model.vo.Result;
+
+import java.util.Map;
+
+/**
+ * 验证码服务接口
+ */
+public interface CaptchaService {
+
+ /**
+ * 生成验证码
+ *
+ * @return 验证码信息
+ */
+ Result> generateCaptcha();
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/service/impl/AuthUserServiceImpl.java b/video-auth/src/main/java/com/nuaa/video/auth/service/impl/AuthUserServiceImpl.java
new file mode 100644
index 0000000..dbcb8f3
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/service/impl/AuthUserServiceImpl.java
@@ -0,0 +1,324 @@
+package com.nuaa.video.auth.service.impl;
+
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.nuaa.video.auth.dto.LoginDTO;
+import com.nuaa.video.auth.dto.RegisterDTO;
+import com.nuaa.video.auth.dto.TokenInfoDTO;
+import com.nuaa.video.auth.entity.AuthUser;
+import com.nuaa.video.auth.entity.AuthUserRole;
+import com.nuaa.video.auth.mapper.AuthUserMapper;
+import com.nuaa.video.auth.mapper.AuthUserRoleMapper;
+import com.nuaa.video.auth.service.AuthUserService;
+import com.nuaa.video.common.model.enums.ResultCode;
+import com.nuaa.video.common.model.vo.Result;
+import com.nuaa.video.auth.utils.JwtUtils;
+import com.nuaa.video.auth.utils.RedisUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 用户认证服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AuthUserServiceImpl extends ServiceImpl implements AuthUserService {
+
+ private final AuthUserMapper authUserMapper;
+ private final AuthUserRoleMapper authUserRoleMapper;
+ private final PasswordEncoder passwordEncoder;
+ private final JwtUtils jwtUtils;
+ private final RedisUtils redisUtils;
+
+ @Value("${jwt.expiration:86400}")
+ private long expiration;
+
+ private static final String USER_TOKEN_PREFIX = "user:token:";
+ private static final String CAPTCHA_PREFIX = "captcha:";
+ private static final String USER_ROLE_PREFIX = "user:role:";
+ private static final String USER_PERMISSION_PREFIX = "user:permission:";
+
+ @Override
+ public Result login(LoginDTO loginDTO) {
+ // 验证验证码
+ if (StringUtils.hasText(loginDTO.getCaptchaKey()) && StringUtils.hasText(loginDTO.getCaptcha())) {
+ String captchaCache = (String) redisUtils.get(CAPTCHA_PREFIX + loginDTO.getCaptchaKey());
+ if (captchaCache == null) {
+ return Result.error(ResultCode.PARAM_ERROR.getCode(), "验证码已过期");
+ }
+ if (!loginDTO.getCaptcha().equalsIgnoreCase(captchaCache)) {
+ return Result.error(ResultCode.PARAM_ERROR.getCode(), "验证码错误");
+ }
+ // 验证通过后移除验证码
+ redisUtils.del(CAPTCHA_PREFIX + loginDTO.getCaptchaKey());
+ }
+
+ // 查询用户
+ AuthUser user = getByUsername(loginDTO.getUsername());
+ if (user == null) {
+ return Result.error(ResultCode.USER_NOT_FOUND.getCode(), "用户名或密码错误");
+ }
+
+ // 校验密码
+ if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
+ return Result.error(ResultCode.PASSWORD_ERROR.getCode(), "用户名或密码错误");
+ }
+
+ // 检查用户状态
+ if (user.getStatus() != 1) {
+ return Result.error(ResultCode.FORBIDDEN.getCode(), "账号已被禁用");
+ }
+
+ // 更新登录信息
+ user.setLastLoginTime(LocalDateTime.now());
+ updateById(user);
+
+ // 生成令牌
+ String accessToken = jwtUtils.generateToken(user.getId(), user.getUsername());
+ String refreshToken = RandomUtil.randomString(32);
+
+ // 存储刷新令牌
+ redisUtils.set(USER_TOKEN_PREFIX + user.getId(), refreshToken, expiration * 2);
+
+ // 缓存用户角色和权限
+ cacheUserAuthInfo(user.getId());
+
+ // 构建返回结果
+ TokenInfoDTO tokenInfo = new TokenInfoDTO()
+ .setAccessToken(accessToken)
+ .setRefreshToken(refreshToken)
+ .setTokenType("Bearer")
+ .setExpiresIn(expiration)
+ .setUserId(user.getId())
+ .setUsername(user.getUsername());
+
+ return Result.success(tokenInfo);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Result register(RegisterDTO registerDTO) {
+ // 验证验证码
+ String captchaCache = (String) redisUtils.get(CAPTCHA_PREFIX + registerDTO.getCaptchaKey());
+ if (captchaCache == null) {
+ return Result.error(ResultCode.PARAM_ERROR.getCode(), "验证码已过期");
+ }
+ if (!registerDTO.getCaptcha().equalsIgnoreCase(captchaCache)) {
+ return Result.error(ResultCode.PARAM_ERROR.getCode(), "验证码错误");
+ }
+
+ // 验证两次密码输入是否一致
+ if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())) {
+ return Result.error(ResultCode.PARAM_ERROR.getCode(), "两次密码输入不一致");
+ }
+
+ // 验证用户名是否已存在
+ AuthUser existUser = getByUsername(registerDTO.getUsername());
+ if (existUser != null) {
+ return Result.error(ResultCode.USER_ALREADY_EXISTS.getCode(), "用户名已存在");
+ }
+
+ // 验证邮箱是否已存在
+ if (StringUtils.hasText(registerDTO.getEmail())) {
+ Long count = count(new LambdaQueryWrapper().eq(AuthUser::getEmail, registerDTO.getEmail()));
+ if (count > 0) {
+ return Result.error(ResultCode.USER_ALREADY_EXISTS.getCode(), "邮箱已被注册");
+ }
+ }
+
+ // 验证手机号是否已存在
+ if (StringUtils.hasText(registerDTO.getPhone())) {
+ Long count = count(new LambdaQueryWrapper().eq(AuthUser::getPhone, registerDTO.getPhone()));
+ if (count > 0) {
+ return Result.error(ResultCode.USER_ALREADY_EXISTS.getCode(), "手机号已被注册");
+ }
+ }
+
+ // 移除验证码
+ redisUtils.del(CAPTCHA_PREFIX + registerDTO.getCaptchaKey());
+
+ // 创建用户
+ AuthUser user = new AuthUser();
+ user.setUsername(registerDTO.getUsername());
+ user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
+ user.setSalt(RandomUtil.randomString(6));
+ user.setEmail(registerDTO.getEmail());
+ user.setPhone(registerDTO.getPhone());
+ user.setStatus(1);
+ user.setCreatedBy("system");
+ user.setUpdatedBy("system");
+ user.setCreateTime(LocalDateTime.now());
+ user.setUpdateTime(LocalDateTime.now());
+
+ // 保存用户
+ save(user);
+
+ // 分配默认角色(普通用户)
+ AuthUserRole userRole = new AuthUserRole();
+ userRole.setUserId(user.getId());
+ userRole.setRoleId(2L); // 普通用户角色ID
+ userRole.setCreatedBy("system");
+ userRole.setCreateTime(LocalDateTime.now());
+ authUserRoleMapper.insert(userRole);
+
+ return Result.success("注册成功");
+ }
+
+ @Override
+ public AuthUser getByUsername(String username) {
+ return getOne(new LambdaQueryWrapper().eq(AuthUser::getUsername, username));
+ }
+
+ @Override
+ public Result refreshToken(String refreshToken) {
+ try {
+ if (!StringUtils.hasText(refreshToken)) {
+ return Result.error(ResultCode.UNAUTHORIZED.getCode(), "刷新令牌无效");
+ }
+
+ // 解析accessToken获取用户ID
+ String accessToken = jwtUtils.getUserIdFromToken(refreshToken).toString();
+ Long userId = Long.parseLong(accessToken);
+
+ // 验证refreshToken是否存在且匹配
+ String cacheRefreshToken = (String) redisUtils.get(USER_TOKEN_PREFIX + userId);
+ if (cacheRefreshToken == null || !cacheRefreshToken.equals(refreshToken)) {
+ return Result.error(ResultCode.UNAUTHORIZED.getCode(), "刷新令牌无效");
+ }
+
+ // 获取用户信息
+ AuthUser user = getById(userId);
+ if (user == null || user.getStatus() != 1) {
+ return Result.error(ResultCode.UNAUTHORIZED.getCode(), "用户不存在或已被禁用");
+ }
+
+ // 生成新令牌
+ String newAccessToken = jwtUtils.generateToken(user.getId(), user.getUsername());
+ String newRefreshToken = RandomUtil.randomString(32);
+
+ // 更新刷新令牌
+ redisUtils.set(USER_TOKEN_PREFIX + user.getId(), newRefreshToken, expiration * 2);
+
+ // 构建返回结果
+ TokenInfoDTO tokenInfo = new TokenInfoDTO()
+ .setAccessToken(newAccessToken)
+ .setRefreshToken(newRefreshToken)
+ .setTokenType("Bearer")
+ .setExpiresIn(expiration)
+ .setUserId(user.getId())
+ .setUsername(user.getUsername());
+
+ return Result.success(tokenInfo);
+ } catch (Exception e) {
+ log.error("刷新令牌失败", e);
+ return Result.error(ResultCode.UNAUTHORIZED.getCode(), "刷新令牌失败");
+ }
+ }
+
+ @Override
+ public Result logout(Long userId) {
+ if (userId == null) {
+ return Result.error(ResultCode.PARAM_ERROR.getCode(), "用户ID不能为空");
+ }
+
+ // 清除用户令牌
+ redisUtils.del(USER_TOKEN_PREFIX + userId);
+
+ // 清除用户权限缓存
+ redisUtils.del(USER_ROLE_PREFIX + userId);
+ redisUtils.del(USER_PERMISSION_PREFIX + userId);
+
+ return Result.success();
+ }
+
+ @Override
+ public Result getCurrentUser(Long userId) {
+ if (userId == null) {
+ return Result.error(ResultCode.PARAM_ERROR.getCode(), "用户ID不能为空");
+ }
+
+ AuthUser user = getById(userId);
+ if (user == null) {
+ return Result.error(ResultCode.USER_NOT_FOUND.getCode(), "用户不存在");
+ }
+
+ // 脱敏处理
+ user.setPassword(null);
+ user.setSalt(null);
+
+ // 获取角色信息
+ List roles = getUserRoles(userId);
+ user.setRoles(roles.toArray(new String[0]));
+
+ return Result.success(user);
+ }
+
+ /**
+ * 获取用户角色
+ *
+ * @param userId 用户ID
+ * @return 角色列表
+ */
+ private List getUserRoles(Long userId) {
+ // 优先从缓存获取
+ @SuppressWarnings("unchecked")
+ List roles = (List) redisUtils.get(USER_ROLE_PREFIX + userId);
+ if (roles == null || roles.isEmpty()) {
+ // 从数据库查询并缓存
+ roles = authUserMapper.getUserRoles(userId);
+ if (!roles.isEmpty()) {
+ redisUtils.set(USER_ROLE_PREFIX + userId, roles, expiration);
+ }
+ }
+ return roles;
+ }
+
+ /**
+ * 获取用户权限
+ *
+ * @param userId 用户ID
+ * @return 权限列表
+ */
+ private List getUserPermissions(Long userId) {
+ // 优先从缓存获取
+ @SuppressWarnings("unchecked")
+ List permissions = (List) redisUtils.get(USER_PERMISSION_PREFIX + userId);
+ if (permissions == null || permissions.isEmpty()) {
+ // 从数据库查询并缓存
+ permissions = authUserMapper.getUserPermissions(userId);
+ if (!permissions.isEmpty()) {
+ redisUtils.set(USER_PERMISSION_PREFIX + userId, permissions, expiration);
+ }
+ }
+ return permissions;
+ }
+
+ /**
+ * 缓存用户认证信息
+ *
+ * @param userId 用户ID
+ */
+ private void cacheUserAuthInfo(Long userId) {
+ // 缓存用户角色
+ List roles = authUserMapper.getUserRoles(userId);
+ if (!roles.isEmpty()) {
+ redisUtils.set(USER_ROLE_PREFIX + userId, roles, expiration);
+ }
+
+ // 缓存用户权限
+ List permissions = authUserMapper.getUserPermissions(userId);
+ if (!permissions.isEmpty()) {
+ redisUtils.set(USER_PERMISSION_PREFIX + userId, permissions, expiration);
+ }
+ }
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/service/impl/CaptchaServiceImpl.java b/video-auth/src/main/java/com/nuaa/video/auth/service/impl/CaptchaServiceImpl.java
new file mode 100644
index 0000000..d2ac90c
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/service/impl/CaptchaServiceImpl.java
@@ -0,0 +1,65 @@
+package com.nuaa.video.auth.service.impl;
+
+import cn.hutool.captcha.CaptchaUtil;
+import cn.hutool.captcha.LineCaptcha;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.util.RandomUtil;
+import com.nuaa.video.auth.service.CaptchaService;
+import com.nuaa.video.common.model.vo.Result;
+import com.nuaa.video.auth.utils.RedisUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 验证码服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CaptchaServiceImpl implements CaptchaService {
+
+ private final RedisUtils redisUtils;
+
+ @Value("${captcha.expiration:60}")
+ private long captchaExpiration;
+
+ private static final String CAPTCHA_PREFIX = "captcha:";
+
+ @Override
+ public Result> generateCaptcha() {
+ try {
+ // 创建验证码
+ LineCaptcha captcha = CaptchaUtil.createLineCaptcha(120, 40, 4, 10);
+
+ // 获取验证码内容和图片
+ String code = captcha.getCode();
+
+ // 生成验证码key
+ String captchaKey = RandomUtil.randomString(16);
+
+ // 将验证码存入Redis
+ redisUtils.set(CAPTCHA_PREFIX + captchaKey, code, captchaExpiration);
+
+ // 转换验证码图片为Base64
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ captcha.write(outputStream);
+ String captchaBase64 = "data:image/png;base64," + Base64.encode(outputStream.toByteArray());
+
+ // 构建返回信息
+ Map result = new HashMap<>();
+ result.put("captchaKey", captchaKey);
+ result.put("captchaImage", captchaBase64);
+
+ return Result.success(result);
+ } catch (Exception e) {
+ log.error("生成验证码失败", e);
+ return Result.error("生成验证码失败");
+ }
+ }
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/utils/JwtUtils.java b/video-auth/src/main/java/com/nuaa/video/auth/utils/JwtUtils.java
new file mode 100644
index 0000000..e83b2f5
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/utils/JwtUtils.java
@@ -0,0 +1,124 @@
+package com.nuaa.video.auth.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * JWT工具类 - auth模块专用
+ */
+@Slf4j
+@Component
+public class JwtUtils {
+
+ @Value("${jwt.secret:video-platform-secret-key-2024-nuaa}")
+ private String secret;
+
+ @Value("${jwt.expiration:86400}")
+ private long expiration;
+
+ @Value("${jwt.token-header:Authorization}")
+ private String tokenHeader;
+
+ /**
+ * 根据用户ID生成JWT
+ */
+ public String generateToken(Long userId, String username) {
+ Map claims = new HashMap<>();
+ claims.put("userId", userId);
+ claims.put("username", username);
+ return createToken(claims, userId.toString());
+ }
+
+ /**
+ * 从JWT中获取用户ID
+ */
+ public Long getUserIdFromToken(String token) {
+ return Long.valueOf(getClaimFromToken(token, Claims::getSubject));
+ }
+
+ /**
+ * 判断JWT是否过期
+ */
+ public boolean isTokenExpired(String token) {
+ final Date expiration = getExpirationDateFromToken(token);
+ return expiration.before(new Date());
+ }
+
+ /**
+ * 验证JWT
+ */
+ public boolean validateToken(String token, String username) {
+ try {
+ final String tokenUsername = getUsernameFromToken(token);
+ return (tokenUsername.equals(username) && !isTokenExpired(token));
+ } catch (Exception e) {
+ log.error("JWT验证失败:", e);
+ return false;
+ }
+ }
+
+ /**
+ * 从JWT中获取用户名
+ */
+ public String getUsernameFromToken(String token) {
+ return getClaimFromToken(token, claims -> claims.get("username", String.class));
+ }
+
+ /**
+ * 获取JWT过期时间
+ */
+ public Date getExpirationDateFromToken(String token) {
+ return getClaimFromToken(token, Claims::getExpiration);
+ }
+
+ /**
+ * 从JWT中获取指定声明
+ */
+ public T getClaimFromToken(String token, Function claimsResolver) {
+ final Claims claims = getAllClaimsFromToken(token);
+ return claimsResolver.apply(claims);
+ }
+
+ /**
+ * 从JWT中获取所有声明
+ */
+ private Claims getAllClaimsFromToken(String token) {
+ return Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(token)
+ .getPayload();
+ }
+
+ /**
+ * 创建JWT
+ */
+ private String createToken(Map claims, String subject) {
+ return Jwts.builder()
+ .claims(claims)
+ .subject(subject)
+ .issuedAt(new Date(System.currentTimeMillis()))
+ .expiration(new Date(System.currentTimeMillis() + expiration * 1000))
+ .signWith(getSigningKey())
+ .compact();
+ }
+
+ /**
+ * 获取签名密钥
+ */
+ private SecretKey getSigningKey() {
+ byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
+ return Keys.hmacShaKeyFor(keyBytes);
+ }
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/utils/RedisUtils.java b/video-auth/src/main/java/com/nuaa/video/auth/utils/RedisUtils.java
new file mode 100644
index 0000000..e091e91
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/utils/RedisUtils.java
@@ -0,0 +1,105 @@
+package com.nuaa.video.auth.utils;
+
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Redis工具类 - auth模块专用
+ */
+@Component
+public class RedisUtils {
+
+ private final RedisTemplate redisTemplate;
+
+ public RedisUtils(RedisTemplate redisTemplate) {
+ this.redisTemplate = redisTemplate;
+ }
+
+ /**
+ * 设置缓存过期时间
+ */
+ public boolean expire(String key, long time) {
+ try {
+ if (time > 0) {
+ redisTemplate.expire(key, time, TimeUnit.SECONDS);
+ }
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * 获取缓存过期时间
+ */
+ public long getExpire(String key) {
+ return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 判断key是否存在
+ */
+ public boolean hasKey(String key) {
+ try {
+ return Boolean.TRUE.equals(redisTemplate.hasKey(key));
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * 删除缓存
+ */
+ public void del(String... key) {
+ if (key != null && key.length > 0) {
+ if (key.length == 1) {
+ redisTemplate.delete(key[0]);
+ } else {
+ redisTemplate.delete((Collection) List.of(key));
+ }
+ }
+ }
+
+ /**
+ * 获取普通缓存
+ */
+ public Object get(String key) {
+ return key == null ? null : redisTemplate.opsForValue().get(key);
+ }
+
+ /**
+ * 设置普通缓存
+ */
+ public boolean set(String key, Object value) {
+ try {
+ redisTemplate.opsForValue().set(key, value);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * 设置普通缓存并设置过期时间
+ */
+ public boolean set(String key, Object value, long time) {
+ try {
+ if (time > 0) {
+ redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+ } else {
+ set(key, value);
+ }
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ // 其他必要的方法可以根据需要添加
+}
\ No newline at end of file
diff --git a/video-auth/src/main/resources/application-dev.yml b/video-auth/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..35589b3
--- /dev/null
+++ b/video-auth/src/main/resources/application-dev.yml
@@ -0,0 +1,26 @@
+spring:
+ datasource:
+ dynamic:
+ primary: master
+ strict: false
+ datasource:
+ master:
+ url: jdbc:mysql://5.180.253.115:3306/video_auth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+ username: root
+ password: sty@G458221
+ driver-class-name: com.mysql.cj.jdbc.Driver
+
+ data:
+ redis:
+ database: 0
+ host: 5.180.253.115
+ port: 6379
+ password: redis_EsJ8FX
+ timeout: 10000
+
+logging:
+ level:
+ com.nuaa.video: debug
+ org.springframework.security: debug
+ org.springframework.beans: debug
+ org.springframework.context: debug
\ No newline at end of file
diff --git a/video-auth/src/main/resources/application.yml b/video-auth/src/main/resources/application.yml
index 1255da4..1f7f282 100644
--- a/video-auth/src/main/resources/application.yml
+++ b/video-auth/src/main/resources/application.yml
@@ -4,35 +4,87 @@ server:
spring:
application:
name: video-auth
+ profiles:
+ active: dev
+ config:
+ import:
+ - optional:nacos:${spring.application.name}.yaml
cloud:
nacos:
discovery:
- server-addr: 5.180.253.115:8848
- namespace: public
- group: DEFAULT_GROUP
+ server-addr: ${NACOS_HOST:5.180.253.115}:${NACOS_PORT:8848}
+ namespace: ${NACOS_NAMESPACE:public}
+ group: ${NACOS_GROUP:DEFAULT_GROUP}
+ config:
+ server-addr: ${spring.cloud.nacos.discovery.server-addr}
+ namespace: ${spring.cloud.nacos.discovery.namespace}
+ group: ${spring.cloud.nacos.discovery.group}
+ file-extension: yaml
sentinel:
transport:
- dashboard: 5.180.253.115:8080
+ dashboard: ${SENTINEL_DASHBOARD_HOST:5.180.253.115}:${SENTINEL_DASHBOARD_PORT:8080}
port: 8720
eager: true
+
datasource:
- url: jdbc:mysql://5.180.253.115:3306/video_auth?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
- username: root
- password: sty@G458221
driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://${MYSQL_HOST:5.180.253.115}:${MYSQL_PORT:3306}/video_auth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+ username: ${MYSQL_USERNAME:root}
+ password: ${MYSQL_PASSWORD:sty@G458221}
+ hikari:
+ minimum-idle: 5
+ maximum-pool-size: 20
+ idle-timeout: 30000
+ max-lifetime: 1200000
+ connection-timeout: 30000
+ connection-test-query: SELECT 1
+
data:
redis:
- host: 5.180.253.115
- port: 6379
- password: redis_EsJ8FX
+ host: ${REDIS_HOST:5.180.253.115}
+ port: ${REDIS_PORT:6379}
+ password: ${REDIS_PASSWORD:redis_EsJ8FX}
database: 0
+ timeout: 10000
+ lettuce:
+ pool:
+ max-active: 200
+ max-wait: -1
+ max-idle: 10
+ min-idle: 0
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
+ type-aliases-package: com.nuaa.video.auth.entity
configuration:
+ map-underscore-to-camel-case: true
+ cache-enabled: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+ global-config:
+ db-config:
+ id-type: auto
+ logic-delete-field: is_deleted
+ logic-delete-value: 1
+ logic-not-delete-value: 0
jwt:
secret: video-platform-secret-key-2024-nuaa
expiration: 86400
- token-header: Authorization
\ No newline at end of file
+ token-header: Authorization
+ token-prefix: "Bearer "
+
+management:
+ endpoints:
+ web:
+ exposure:
+ include: '*'
+ endpoint:
+ health:
+ show-details: always
+
+logging:
+ level:
+ com.nuaa.video: info
+ org.springframework.security: info
+ org.springframework.beans: debug
+ org.springframework.context: debug
\ No newline at end of file
diff --git a/video-auth/src/main/resources/mapper/AuthPermissionMapper.xml b/video-auth/src/main/resources/mapper/AuthPermissionMapper.xml
new file mode 100644
index 0000000..46a4293
--- /dev/null
+++ b/video-auth/src/main/resources/mapper/AuthPermissionMapper.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/video-auth/src/main/resources/mapper/AuthRoleMapper.xml b/video-auth/src/main/resources/mapper/AuthRoleMapper.xml
new file mode 100644
index 0000000..b063b49
--- /dev/null
+++ b/video-auth/src/main/resources/mapper/AuthRoleMapper.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/video-auth/src/main/resources/mapper/AuthRolePermissionMapper.xml b/video-auth/src/main/resources/mapper/AuthRolePermissionMapper.xml
new file mode 100644
index 0000000..64bb061
--- /dev/null
+++ b/video-auth/src/main/resources/mapper/AuthRolePermissionMapper.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/video-auth/src/main/resources/mapper/AuthUserMapper.xml b/video-auth/src/main/resources/mapper/AuthUserMapper.xml
new file mode 100644
index 0000000..066fd0b
--- /dev/null
+++ b/video-auth/src/main/resources/mapper/AuthUserMapper.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ SELECT
+ r.code
+ FROM
+ auth_user_role ur
+ INNER JOIN auth_role r ON ur.role_id = r.id
+ WHERE
+ ur.user_id = #{userId}
+
+
+
+
+ SELECT
+ p.code
+ FROM
+ auth_user_role ur
+ INNER JOIN auth_role_permission rp ON ur.role_id = rp.role_id
+ INNER JOIN auth_permission p ON rp.permission_id = p.id
+ WHERE
+ ur.user_id = #{userId}
+
+
\ No newline at end of file
diff --git a/video-common/src/main/java/com/nuaa/video/common/config/MybatisPlusConfig.java b/video-common/src/main/java/com/nuaa/video/common/config/MybatisPlusConfig.java
index 1e1653b..c994d23 100644
--- a/video-common/src/main/java/com/nuaa/video/common/config/MybatisPlusConfig.java
+++ b/video-common/src/main/java/com/nuaa/video/common/config/MybatisPlusConfig.java
@@ -1,4 +1,3 @@
-// com.nuaa.video.common.config.MybatisPlusConfig.java
package com.nuaa.video.common.config;
import com.baomidou.mybatisplus.annotation.DbType;