test05: 兼容mysql以及切换到java25

This commit is contained in:
awin-x 2025-12-01 19:56:37 +08:00
parent 18df3147c0
commit 492b497d63
22 changed files with 947 additions and 135 deletions

130
test05/USER_ROLE_GUIDE.md Normal file
View File

@ -0,0 +1,130 @@
# 用户角色访问控制指南
## 用户名格式规则
- **管理员**: `a_<姓名>` (例如: `a_admin`, `a_zhangsan`)
- **老师**: `t_<姓名>` (例如: `t_teacher`, `t_lisi`)
- **学生**: `s_<姓名>` (例如: `s_student`, `s_wangwu`)
## 权限说明
### 管理员权限
- 可以访问所有API端点
- 可以创建老师用户
- 可以创建学生用户
- 可以创建管理员用户
### 老师权限
- 可以访问老师端API (`/teacherAction/**`)
- 可以访问学生端API (`/studentAction/**`)
- 可以创建学生用户
- 不能创建老师或管理员用户
### 学生权限
- 只能访问学生端API (`/studentAction/**`)
- 不能创建任何用户
## API端点
### 用户管理API (`/userManagementAction/**`)
#### 创建老师用户
```
POST /userManagementAction/createTeacher
参数: username, password
权限: 仅管理员
```
#### 创建学生用户
```
POST /userManagementAction/createStudent
参数: username, password
权限: 管理员和老师
```
#### 创建管理员用户
```
POST /userManagementAction/createAdmin
参数: username, password
权限: 仅管理员
```
### 课程管理API示例 (`/courseAction/**`)
#### 选课API (学生端)
```
POST /courseAction/selectCourse
参数: courseId
权限: 学生
状态: 待实现
```
#### 创建课程API (老师端)
```
POST /courseAction/createCourse
参数: courseName, courseDescription
权限: 老师
状态: 待实现
```
#### 获取课程列表
```
GET /courseAction/getCourses
参数: page, pageSize
权限: 所有用户
状态: 待实现
```
## 使用示例
### 1. 管理员登录
```bash
# 假设已存在管理员用户 a_admin
POST /userAction/login
{
"username": "a_admin",
"password": "password"
}
```
### 2. 管理员创建老师用户
```bash
POST /userManagementAction/createTeacher
Headers: token: <登录返回的JWT>
{
"username": "t_zhangsan",
"password": "password123"
}
```
### 3. 老师创建学生用户
```bash
POST /userManagementAction/createStudent
Headers: token: <老师登录返回的JWT>
{
"username": "s_lisi",
"password": "password123"
}
```
## 注意事项
1. 所有API调用都需要在请求头中包含有效的JWT token
2. 用户名必须严格按照格式要求,否则会被拒绝
3. 拦截器会自动验证用户角色权限
4. 课程相关API目前只是空实现需要创建相应的数据库表后才能完成功能
## 数据库表需求
为了完整实现课程功能,需要创建以下表:
1. **课程表** (courses)
- id, course_name, course_description, teacher_id, create_time, etc.
2. **选课表** (student_courses)
- id, student_id, course_id, select_time, etc.
3. **课程老师映射表** (如果需要多对多关系)
- id, course_id, teacher_id, etc.
这些表创建后就可以完成课程管理API的具体实现了。

View File

@ -1,148 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<!-- 父工程配置Spring Boot 3.4.0 稳定版(兼容 JDK 25 -->
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version> <version>3.5.0</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- 从仓库查找父工程 -->
</parent> </parent>
<!-- 项目基本信息 -->
<groupId>top.awin-x</groupId> <groupId>top.awin-x</groupId>
<artifactId>test05</artifactId> <artifactId>test05</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>test05</name> <name>test05</name>
<description>test05</description> <description>Test project for Spring Boot (JDK 25)</description>
<url/> <url>https://github.com/awin-x/test05</url> <!-- 可替换为实际仓库地址 -->
<!-- 许可证信息(可选,规范项目) -->
<licenses> <licenses>
<license/> <license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses> </licenses>
<!-- 开发者信息(可选) -->
<developers> <developers>
<developer/> <developer>
<id>awin-x</id>
<name>awin-x</name>
<email>awin-x@example.com</email> <!-- 可替换为实际邮箱 -->
<roles>
<role>Developer</role>
</roles>
<timezone>+8</timezone>
</developer>
</developers> </developers>
<!-- SCM 版本控制信息(可选) -->
<scm> <scm>
<connection/> <connection>scm:git:https://github.com/awin-x/test05.git</connection>
<developerConnection/> <developerConnection>scm:git:git@github.com:awin-x/test05.git</developerConnection>
<tag/> <url>https://github.com/awin-x/test05</url>
<url/> <tag>HEAD</tag>
</scm> </scm>
<!-- 全局属性配置 -->
<properties> <properties>
<java.version>21</java.version> <java.version>25</java.version> <!-- 统一 JDK 版本为 25 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 依赖版本统一管理(便于后续维护) -->
<mybatis-spring-boot.version>3.0.4</mybatis-spring-boot.version>
<druid.version>1.2.24</druid.version>
<sqlite-jdbc.version>3.46.1.0</sqlite-jdbc.version>
<mysql-connector.version>8.4.0</mysql-connector.version>
<jjwt.version>0.12.6</jjwt.version>
<fastjson2.version>2.0.53</fastjson2.version>
<lombok.version>1.18.38</lombok.version>
</properties> </properties>
<!-- 使用外置tomcat -->
<!-- <packaging>war</packaging>-->
<dependencies> <dependencies>
<!-- Spring Web 核心依赖(内置 Tomcat -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<!-- MyBatis 整合 Spring Boot -->
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version> <version>${mybatis-spring-boot.version}</version>
</dependency> </dependency>
<!-- 数据库连接池DruidSpring Boot 3 适配版) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- SQLite 驱动 -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>${sqlite-jdbc.version}</version>
</dependency>
<!-- MySQL 驱动8.4+ 兼容 JDK 21+ -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId> <!-- 新版本artifactId变更原mysql-connector-java -->
<version>${mysql-connector.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Lombok 简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional> <!-- 不传递依赖 -->
</dependency>
<!-- JWT 认证 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- FastJSON2 序列化 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<!-- Spring AOP 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- MyBatis 测试支持 -->
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId> <artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.4</version> <version>${mybatis-spring-boot.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.47.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.12.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.53</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.realityforge.org.jetbrains.annotations/org.jetbrains.annotations -->
<dependency>
<groupId>org.realityforge.org.jetbrains.annotations</groupId>
<artifactId>org.jetbrains.annotations</artifactId>
<version>1.7.0</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- &lt;!&ndash;移除tomcat依赖&ndash;&gt;-->
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-tomcat</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
<!-- </dependency>-->
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<!-- Spring Boot 打包插件支持可执行jar/war -->
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 默认激活开发环境profile -->
<profiles>
<profile>dev</profile>
</profiles>
<!-- 允许 Lombok 注解在打包时生效 -->
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin> </plugin>
<!-- Maven 编译插件(适配 JDK 25 -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version> <version>3.14.1</version> <!-- 最新稳定版,兼容 JDK 25 -->
<configuration> <configuration>
<source>25</source> <!-- 根据你的JDK版本调整 --> <source>${java.version}</source> <!-- 源码 JDK 版本 -->
<target>25</target> <target>${java.version}</target> <!-- 编译目标 JDK 版本 -->
<encoding>${project.build.sourceEncoding}</encoding>
<!-- Lombok 注解处理器(确保编译时识别注解) -->
<annotationProcessorPaths> <annotationProcessorPaths>
<path> <path>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.24</version> <version>${lombok.version}</version>
</path>
<!-- 如需 MyBatis 注解处理器(如 @Mapper可在此添加 -->
<path>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</path> </path>
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
<!-- 可选:测试报告插件(生成规范测试报告) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<skipTests>false</skipTests>
<testFailureIgnore>false</testFailureIgnore>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<!-- 可选:外置 Tomcat 支持(如需部署到外部 Tomcat解开以下注释 -->
<!--
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
-->
</project> </project>

View File

@ -2,21 +2,59 @@ package test05.config;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import test05.interceptor.LoginCheckInterceptor; import test05.interceptor.LoginCheckInterceptor;
/**
* 配置类
*/
@Configuration @Configuration
public class WebConfig implements WebMvcConfigurer { public class WebConfig implements WebMvcConfigurer {
private final LoginCheckInterceptor loginCheckInterceptor; private final LoginCheckInterceptor loginCheckInterceptor;
private final Environment environment;
// 构造函数注入登陆检查拦截器
@Autowired @Autowired
WebConfig(LoginCheckInterceptor loginCheckInterceptor){ WebConfig(LoginCheckInterceptor loginCheckInterceptor, Environment environment){
this.loginCheckInterceptor = loginCheckInterceptor; this.loginCheckInterceptor = loginCheckInterceptor;
this.environment = environment;
} }
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/*Action/**").excludePathPatterns("/userAction/login"); registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/*Action/**")
.excludePathPatterns("/userAction/login", "/userAction/logout");
}
@Override
public void addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry registry) {
// 仅在开发环境启用跨域
String[] activeProfiles = environment.getActiveProfiles();
boolean isDev = false;
if (activeProfiles.length == 0) {
// 如果没有指定profile默认使用dev
isDev = true;
} else {
for (String profile : activeProfiles) {
if ("dev".equals(profile) || "development".equals(profile) || "test".equals(profile)) {
isDev = true;
break;
}
}
}
if (isDev) {
registry.addMapping("/**") // 允许所有路径
.allowedOriginPatterns("*") // 允许所有来源
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的HTTP方法
.allowedHeaders("*") // 允许所有请求头
.allowCredentials(true) // 允许发送cookie
.maxAge(3600); // 预检请求的有效期
}
} }
} }

View File

@ -0,0 +1,63 @@
package test05.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import test05.pojo.Result;
/**
* 课程管理控制器 - 提供示例API
*/
@RestController
@RequestMapping("/courseAction")
public class CourseManagementController {
/**
* 选课API - 学生端API示例
* 注意这是空实现因为还没有选课表
*/
@RequestMapping("/selectCourse")
public Result selectCourse(Integer courseId, HttpServletRequest request) {
// TODO: 实现选课功能
// 需要创建选课表和相关的映射关系表
return Result.success("选课功能待实现 - 课程ID: " + courseId);
}
/**
* 创建课程API - 老师端API示例
* 注意这是空实现因为还没有课程表和老师用户映射关系表
*/
@RequestMapping("/createCourse")
public Result createCourse(String courseName, String courseDescription, HttpServletRequest request) {
// TODO: 实现创建课程功能
// 需要创建课程表和课程与老师的映射关系表
return Result.success("创建课程功能待实现 - 课程名称: " + courseName);
}
/**
* 获取课程列表API - 公共API示例
*/
@RequestMapping("/getCourses")
public Result getCourses(Integer page, Integer pageSize) {
// TODO: 实现获取课程列表功能
return Result.success("获取课程列表功能待实现");
}
/**
* 删除课程API - 老师端API示例
*/
@RequestMapping("/deleteCourse")
public Result deleteCourse(Integer courseId, HttpServletRequest request) {
// TODO: 实现删除课程功能
return Result.success("删除课程功能待实现 - 课程ID: " + courseId);
}
/**
* 更新课程API - 老师端API示例
*/
@RequestMapping("/updateCourse")
public Result updateCourse(Integer courseId, String courseName, String courseDescription, HttpServletRequest request) {
// TODO: 实现更新课程功能
return Result.success("更新课程功能待实现 - 课程ID: " + courseId);
}
}

View File

@ -0,0 +1,56 @@
package test05.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import test05.pojo.Result;
import test05.pojo.Teacher;
import test05.service.TeacherService;
@RestController
@RequestMapping("/teacherAction")
public class TeacherController {
private final TeacherService teacherService;
@Autowired
public TeacherController(TeacherService teacherService) {
this.teacherService = teacherService;
}
@RequestMapping("/get")
public Result get(Teacher teacher, Integer page, Integer pageSize) {
if (pageSize == null) {
pageSize = 10;
}
if (page == null) {
page = 1;
}
int offset = Math.max(0, (page - 1)) * pageSize;
return teacherService.getTeacher(teacher, offset, pageSize);
}
@RequestMapping("/add")
public Result add(Teacher teacher) {
if (teacher == null || teacher.getId() != -1) {
return Result.error("bad request");
}else{
return teacherService.addTeacher(teacher);
}
}
@RequestMapping("/edit")
public Result edit(Teacher teacher) {
if (teacher == null || teacher.getId() == -1) {
return Result.error("bad request");
}
return teacherService.updateTeacher(teacher);
}
@RequestMapping("/delete")
public Result delete(Teacher teacher) {
if (teacher == null || teacher.getId() <= 0) {
return Result.error("bad request");
}
return teacherService.deleteTeacher(teacher);
}
}

View File

@ -0,0 +1,106 @@
package test05.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import test05.enums.UserRole;
import test05.pojo.Result;
import test05.pojo.User;
import test05.service.UserService;
import test05.utils.UserRoleUtils;
/**
* 用户管理控制器 - 处理用户创建功能
*/
@RestController
@RequestMapping("/userManagementAction")
public class UserManagementController {
private final UserService userService;
@Autowired
public UserManagementController(UserService userService) {
this.userService = userService;
}
/**
* 创建老师用户 - 仅管理员可访问
*/
@RequestMapping("/createTeacher")
public Result createTeacher(String username, String password, HttpServletRequest request) {
String token = request.getHeader("token");
UserRole creatorRole = UserRoleUtils.getUserRoleFromToken(token);
// 检查权限
if (!UserRoleUtils.canCreateUser(creatorRole, UserRole.TEACHER)) {
return Result.error("access denied");
}
// 验证用户名格式
if (!username.startsWith(UserRole.TEACHER.getPrefix())) {
return Result.error("teacher username must start with 't_'");
}
if (!UserRole.isValidUsername(username)) {
return Result.error("invalid username format");
}
// 创建用户
User newUser = new User(username, password);
return userService.addUser(newUser);
}
/**
* 创建学生用户 - 管理员和老师可访问
*/
@RequestMapping("/createStudent")
public Result createStudent(String username, String password, HttpServletRequest request) {
String token = request.getHeader("token");
UserRole creatorRole = UserRoleUtils.getUserRoleFromToken(token);
// 检查权限
if (!UserRoleUtils.canCreateUser(creatorRole, UserRole.STUDENT)) {
return Result.error("access denied");
}
// 验证用户名格式
if (!username.startsWith(UserRole.STUDENT.getPrefix())) {
return Result.error("student username must start with 's_'");
}
if (!UserRole.isValidUsername(username)) {
return Result.error("invalid username format");
}
// 创建用户
User newUser = new User(username, password);
return userService.addUser(newUser);
}
/**
* 创建管理员用户 - 仅管理员可访问
*/
@RequestMapping("/createAdmin")
public Result createAdmin(String username, String password, HttpServletRequest request) {
String token = request.getHeader("token");
UserRole creatorRole = UserRoleUtils.getUserRoleFromToken(token);
// 只有管理员可以创建管理员
if (creatorRole != UserRole.ADMIN) {
return Result.error("access denied");
}
// 验证用户名格式
if (!username.startsWith(UserRole.ADMIN.getPrefix())) {
return Result.error("admin username must start with 'a_'");
}
if (!UserRole.isValidUsername(username)) {
return Result.error("invalid username format");
}
// 创建用户
User newUser = new User(username, password);
return userService.addUser(newUser);
}
}

View File

@ -0,0 +1,73 @@
package test05.enums;
/**
* 用户角色枚举
*/
public enum UserRole {
ADMIN("a_", "管理员"),
TEACHER("t_", "老师"),
STUDENT("s_", "学生");
private final String prefix;
private final String description;
UserRole(String prefix, String description) {
this.prefix = prefix;
this.description = description;
}
public String getPrefix() {
return prefix;
}
public String getDescription() {
return description;
}
/**
* 根据用户名获取角色
*/
public static UserRole getRoleByUsername(String username) {
if (username == null || username.isEmpty()) {
return null;
}
if (username.startsWith(ADMIN.getPrefix())) {
return ADMIN;
} else if (username.startsWith(TEACHER.getPrefix())) {
return TEACHER;
} else if (username.startsWith(STUDENT.getPrefix())) {
return STUDENT;
}
return null;
}
/**
* 检查用户名格式是否有效
*/
public static boolean isValidUsername(String username) {
return getRoleByUsername(username) != null;
}
/**
* 检查角色是否有权限访问指定路径
*/
public boolean hasAccessToPath(String path) {
if (path == null) return false;
switch (this) {
case ADMIN:
// 管理员可以访问所有API
return true;
case TEACHER:
// 老师可以访问老师端API
return path.contains("/teacherAction/") || path.contains("/studentAction/");
case STUDENT:
// 学生只能访问学生端API
return path.contains("/studentAction/");
default:
return false;
}
}
}

View File

@ -3,20 +3,35 @@ package test05.interceptor;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import test05.enums.UserRole;
import test05.pojo.Result; import test05.pojo.Result;
import test05.utils.JwtUtils; import test05.utils.JwtUtils;
import test05.utils.UserRoleUtils;
@Component @Component
public class LoginCheckInterceptor implements HandlerInterceptor { public class LoginCheckInterceptor implements HandlerInterceptor {
@Override @Override
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
String token = request.getHeader("token"); String token = request.getHeader("token");
try{ try{
JwtUtils.parseJwt(token); JwtUtils.parseJwt(token);
// 检查角色权限
UserRole userRole = UserRoleUtils.getUserRoleFromToken(token);
if (userRole == null) {
response.getWriter().print(JSONObject.toJSONString(Result.error("invalid user role")));
return false;
}
String requestURI = request.getRequestURI();
if (!userRole.hasAccessToPath(requestURI)) {
response.getWriter().print(JSONObject.toJSONString(Result.error("access denied")));
return false;
}
}catch(Exception e){ }catch(Exception e){
response.getWriter().print(JSONObject.toJSONString(Result.error("not login"))); response.getWriter().print(JSONObject.toJSONString(Result.error("not login")));
return false; return false;
@ -24,11 +39,11 @@ public class LoginCheckInterceptor implements HandlerInterceptor {
return true; return true;
} }
@Override @Override
public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) { public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
} }
@Override @Override
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
} }
} }

View File

@ -7,7 +7,8 @@ import java.util.List;
@Mapper @Mapper
public interface TeacherMapper { public interface TeacherMapper {
List<Teacher> selectTeacher(Teacher teacher); List<Teacher> selectTeacher(Teacher teacher, int offset, int pageSize);
int selectTeacherCount(Teacher teacher);
int insertTeacher(Teacher teacher); int insertTeacher(Teacher teacher);
int updateTeacher(Teacher teacher); int updateTeacher(Teacher teacher);
int deleteTeacher(Teacher teacher); int deleteTeacher(Teacher teacher);

View File

@ -10,18 +10,24 @@ import lombok.NonNull;
@NoArgsConstructor @NoArgsConstructor
public class Teacher { public class Teacher {
@NonNull @NonNull
Integer N_TEACHER_ID=-1; // Integer N_TEACHER_ID=-1;
Integer id = -1;
@NonNull @NonNull
String VC_TEACHER_NAME=""; // String VC_TEACHER_NAME="";
String name = "";
@NonNull @NonNull
Integer N_SEX=-1; // Integer N_SEX=-1;
Integer sex = -1;
@NonNull @NonNull
String VC_BIRTH=""; // String VC_BIRTH="";
String birth = "";
@NonNull @NonNull
Integer N_EDUC=-1; // Integer N_EDUC=-1;
Integer educ = -1;
@NonNull @NonNull
Integer N_TITLE=-1; // Integer N_TITLE=-1;
Integer title = -1;
public Teacher(int id){ public Teacher(int id){
N_TEACHER_ID = id; this.id = id;
} }
} }

View File

@ -0,0 +1,11 @@
package test05.service;
import test05.pojo.Result;
import test05.pojo.Teacher;
public interface TeacherService {
Result getTeacher(Teacher teacher, int page, int pageSize);
Result addTeacher(Teacher teacher);
Result updateTeacher(Teacher teacher);
Result deleteTeacher(Teacher teacher);
}

View File

@ -10,4 +10,11 @@ public interface UserService {
* @return 登录结果 * @return 登录结果
*/ */
Result login(User user); Result login(User user);
/**
* 添加用户服务
* @param user 要添加的用户
* @return 添加结果
*/
Result addUser(User user);
} }

View File

@ -0,0 +1,53 @@
package test05.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import test05.mapper.TeacherMapper;
import test05.pojo.Result;
import test05.pojo.Teacher;
import test05.service.TeacherService;
@Service
public class TeacherServiceImpl implements TeacherService{
private final TeacherMapper teacherMapper;
@Autowired
public TeacherServiceImpl(TeacherMapper teacherMapper) {
this.teacherMapper = teacherMapper;
}
@Override
public Result getTeacher(Teacher teacher, int page, int pageSize) {
try{
int offset = Math.max(0, (page - 1)) * pageSize;
List<Teacher> list = teacherMapper.selectTeacher(teacher, offset, pageSize);
int total = teacherMapper.selectTeacherCount(teacher);
return Result.success(list, total);
}catch (Exception e){
return Result.error(e.getMessage());
}
}
@Override
public Result addTeacher(Teacher teacher) {
// TODO校验数据
int count = teacherMapper.insertTeacher(teacher);
return Result.success(count>0);
}
@Override
public Result updateTeacher(Teacher teacher) {
int count = teacherMapper.updateTeacher(teacher);
return Result.success(count>0);
}
@Override
public Result deleteTeacher(Teacher teacher) {
int count = teacherMapper.deleteTeacher(teacher);
return Result.success(count>0);
}
}

View File

@ -34,4 +34,21 @@ public class UserServiceImpl implements UserService {
return Result.error("password incorrect"); return Result.error("password incorrect");
} }
} }
@Override
public Result addUser(User user) {
// 检查用户名是否已存在
User existingUser = userMapper.selectUserByName(user.getName());
if (existingUser != null) {
return Result.error("username already exists");
}
// 插入新用户
int result = userMapper.insertUser(user);
if (result > 0) {
return Result.success("user created successfully");
} else {
return Result.error("failed to create user");
}
}
} }

View File

@ -0,0 +1,113 @@
package test05.utils;
import java.util.Map;
import com.alibaba.fastjson2.JSONObject;
import test05.enums.UserRole;
/**
* 用户角色工具类
*/
public class UserRoleUtils {
/**
* 从JWT token中提取用户名并获取角色
*/
public static UserRole getUserRoleFromToken(String token) {
try {
if (token == null || token.isEmpty()) {
return null;
}
// 解析JWT token获取用户信息
Map<String, Object> claims_map = test05.utils.JwtUtils.parseJwt(token);
com.alibaba.fastjson2.JSONObject claims = new JSONObject(claims_map) ;
if (claims == null || !claims.containsKey("data")) {
return null;
}
Object dataObj = claims.get("data");
if (dataObj == null) {
return null;
}
com.alibaba.fastjson2.JSONObject data;
if (dataObj instanceof com.alibaba.fastjson2.JSONObject) {
data = (com.alibaba.fastjson2.JSONObject) dataObj;
} else {
data = com.alibaba.fastjson2.JSONObject.parseObject(dataObj.toString());
}
if (!data.containsKey("name")) {
return null;
}
String username = data.getString("name");
return UserRole.getRoleByUsername(username);
} catch (Exception e) {
return null;
}
}
/**
* 从JWT token中提取用户名
*/
public static String getUsernameFromToken(String token) {
try {
if (token == null || token.isEmpty()) {
return null;
}
com.alibaba.fastjson2.JSONObject claims = new JSONObject(test05.utils.JwtUtils.parseJwt(token));
if (claims == null || !claims.containsKey("data")) {
return null;
}
Object dataObj = claims.get("data");
if (dataObj == null) {
return null;
}
com.alibaba.fastjson2.JSONObject data;
if (dataObj instanceof com.alibaba.fastjson2.JSONObject) {
data = (com.alibaba.fastjson2.JSONObject) dataObj;
} else {
data = com.alibaba.fastjson2.JSONObject.parseObject(dataObj.toString());
}
if (!data.containsKey("name")) {
return null;
}
return data.getString("name");
} catch (Exception e) {
return null;
}
}
/**
* 检查用户是否有权限创建指定角色的用户
*/
public static boolean canCreateUser(UserRole creatorRole, UserRole targetRole) {
if (creatorRole == null || targetRole == null) {
return false;
}
switch (creatorRole) {
case ADMIN:
// 管理员可以创建老师和用户
return targetRole == UserRole.TEACHER || targetRole == UserRole.STUDENT;
case TEACHER:
// 老师可以创建学生
return targetRole == UserRole.STUDENT;
case STUDENT:
// 学生不能创建用户
return false;
default:
return false;
}
}
}

View File

@ -0,0 +1,16 @@
# 开发环境配置
# 开发环境数据库配置
spring.datasource.url=jdbc:mysql://www.awin-x.top:63306/db_student
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=admin
spring.datasource.password=3131313xY
spring.datasource.druid.validation-query=SELECT 1
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开发环境特定配置
logging.level.root=INFO
logging.level.test05=DEBUG
spring.devtools.restart.enabled=true

View File

@ -0,0 +1,16 @@
# 生产环境配置
# 生产环境数据库配置(请根据实际情况修改)
spring.datasource.url=jdbc:mysql://your-prod-db-host:3306/db_student
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=prod_user
spring.datasource.password=prod_password
spring.datasource.druid.validation-query=SELECT 1
# 生产环境关闭SQL日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.nologging.NoLoggingImpl
# 生产环境特定配置
logging.level.root=WARN
spring.devtools.restart.enabled=false

View File

@ -1,15 +1,7 @@
spring.application.name=test05 spring.application.name=test05
#\u4F7F\u7528\u672C\u5730\u6D4B\u8BD5\u6570\u636E\u5E93 # 默认激活开发环境profile
#spring.datasource.driver-class-name=org.sqlite.JDBC spring.profiles.active=dev
spring.datasource.url=jdbc:mysql://www.awin-x.top:63306/db_student
#spring.datasource.url=jdbc:sqlite::resource:db/db_student.db
##\u4F7F\u7528mysql\u6570\u636E\u5E93
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.datasource.url=jdbc:mysql://localhost:3306/db_student.db
spring.datasource.username=admin
spring.datasource.password=3131313xY
# 公共配置(可被各环境覆盖)
spring.datasource.druid.validation-query=SELECT 1 spring.datasource.druid.validation-query=SELECT 1
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

View File

@ -22,7 +22,7 @@
AND (VC_DETAIL LIKE CONCAT('%', #{student.detail}, '%') AND (VC_DETAIL LIKE CONCAT('%', #{student.detail}, '%')
OR (#{student.detail} = '' AND VC_DETAIL IS NULL)) OR (#{student.detail} = '' AND VC_DETAIL IS NULL))
ORDER BY N_STUDENT_ID DESC ORDER BY N_STUDENT_ID DESC
LIMIT #{offset},#{pageSize} LIMIT #{offset},#{pageSize};
</select> </select>
<select id="selectStudentCount" resultType="int"> <select id="selectStudentCount" resultType="int">
SELECT COUNT(*) SELECT COUNT(*)

View File

@ -4,31 +4,46 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test05.mapper.TeacherMapper"> <mapper namespace="test05.mapper.TeacherMapper">
<select id="selectTeacher" resultType="test05.pojo.Teacher"> <select id="selectTeacher" resultType="test05.pojo.Teacher">
SELECT * SELECT N_TEACHER_ID AS id,
VC_TEACHER_NAME AS name,
N_SEX AS sex,
VC_BIRTH AS birth,
N_EDUC AS educ,
N_TITLE AS title
FROM t_teacher FROM t_teacher
WHERE #{N_TEACHER_ID} IN (N_TEACHER_ID, -1) WHERE #{teacher.id} in (N_TEACHER_ID, -1)
AND VC_TEACHER_NAME LIKE CONCAT('%', #{VC_TEACHER_NAME}, '%') AND VC_TEACHER_NAME LIKE CONCAT('%', #{teacher.name}, '%')
AND #{N_SEX} IN (N_SEX, -1) AND #{teacher.sex} in (N_SEX, -1)
AND VC_BIRTH LIKE CONCAT('%', #{VC_BIRTH}, '%') AND #{teacher.educ} in (N_EDUC, -1)
AND #{N_EDUC} IN (N_EDUC, -1) AND #{teacher.title} in (N_TITLE, -1)
AND #{N_TITLE} IN (N_TITLE, -1) ORDER BY N_TEACHER_ID DESC
LIMIT #{offset},#{pageSize};
</select>
<select id="selectTeacherCount" resultType="int">
SELECT COUNT(*)
FROM t_teacher
WHERE #{id} in (N_TEACHER_ID, -1)
AND VC_TEACHER_NAME LIKE CONCAT('%', #{name}, '%')
AND #{sex} in (N_SEX, -1)
AND #{educ} in (N_EDUC, -1)
AND #{title} in (N_TITLE, -1);
</select> </select>
<insert id="insertTeacher" keyProperty="N_TEACHER_ID" useGeneratedKeys="true"> <insert id="insertTeacher" keyProperty="N_TEACHER_ID" useGeneratedKeys="true">
INSERT INTO t_teacher INSERT INTO t_teacher
(VC_TEACHER_NAME, N_SEX, VC_BIRTH, N_EDUC, N_TITLE) (VC_TEACHER_NAME, N_SEX, VC_BIRTH, N_EDUC, N_TITLE)
VALUES (#{VC_TEACHER_NAME},#{N_SEX},#{VC_BIRTH},#{N_EDUC},#{N_TITLE}) VALUES (#{name},#{sex},#{birth},#{educ},#{title});
</insert> </insert>
<update id="updateTeacher"> <update id="updateTeacher">
UPDATE t_teacher UPDATE t_teacher
SET VC_TEACHER_NAME=#{VC_TEACHER_NAME}, SET VC_TEACHER_NAME=#{name},
N_SEX=#{N_SEX}, N_SEX=#{sex},
VC_BIRTH=#{VC_BIRTH}, VC_BIRTH=#{birth},
N_EDUC=#{N_EDUC}, N_EDUC=#{educ},
N_TITLE=#{N_TITLE} N_TITLE=#{title}
WHERE N_TEACHER_ID = #{N_TEACHER_ID} WHERE N_TEACHER_ID=#{N_TEACHER_ID};
</update> </update>
<delete id="deleteTeacher"> <delete id="deleteTeacher">
DELETE FROM t_teacher DELETE FROM t_teacher
WHERE N_TEACHER_ID = #{N_TEACHER_ID} WHERE N_TEACHER_ID = #{id};
</delete> </delete>
</mapper> </mapper>

View File

@ -104,40 +104,40 @@ class Test05ApplicationTests {
@Test @Test
public void teacherMapperTest() { public void teacherMapperTest() {
Teacher teacher = new Teacher(); Teacher teacher = new Teacher();
teacher.setVC_TEACHER_NAME("测试老师"); teacher.setName("测试老师");
teacher.setN_EDUC(2); teacher.setEduc(2);
teacher.setN_TITLE(2); teacher.setTitle(2);
teacher.setVC_BIRTH("20230810"); teacher.setBirth("20230810");
teacher.setN_SEX(1); teacher.setSex(1);
//添加老师 //添加老师
teacherMapper.insertTeacher(teacher); teacherMapper.insertTeacher(teacher);
System.out.println(teacher.getN_TEACHER_ID()); System.out.println(teacher.getId());
if (teacher.getN_TEACHER_ID() == -1) { if (teacher.getId() == -1) {
System.out.println("老师信息添加错误"); System.out.println("老师信息添加错误");
throw new TestAbortedException(); throw new TestAbortedException();
} }
//查询老师 //查询老师
List<Teacher> selectResult = teacherMapper.selectTeacher(new Teacher()); List<Teacher> selectResult = teacherMapper.selectTeacher(new Teacher(), 0, 10);
if (selectResult.isEmpty()) { if (selectResult.isEmpty()) {
System.out.println("查询老师信息出现错误"); System.out.println("查询老师信息出现错误");
throw new TestAbortedException(); throw new TestAbortedException();
} }
System.out.println(selectResult); System.out.println(selectResult);
teacher.setN_SEX(0); teacher.setSex(0);
//修改老师 //修改老师
teacherMapper.updateTeacher(teacher); teacherMapper.updateTeacher(teacher);
selectResult = teacherMapper.selectTeacher(teacher); selectResult = teacherMapper.selectTeacher(teacher, 0, 10);
if (selectResult.size() != 1) { if (selectResult.size() != 1) {
System.out.println("查询老师信息出现错误"); System.out.println("查询老师信息出现错误");
throw new TestAbortedException(); throw new TestAbortedException();
} }
if (selectResult.getFirst().getN_SEX() != 0) { if (selectResult.getFirst().getSex() != 0) {
System.out.println("老师信息修改出现错误"); System.out.println("老师信息修改出现错误");
throw new TestAbortedException(); throw new TestAbortedException();
} }
//删除老师 //删除老师
teacherMapper.deleteTeacher(teacher); teacherMapper.deleteTeacher(teacher);
selectResult = teacherMapper.selectTeacher(teacher); selectResult = teacherMapper.selectTeacher(teacher, 0, 10);
if (!selectResult.isEmpty()) { if (!selectResult.isEmpty()) {
System.out.println("老师信息删除出现错误"); System.out.println("老师信息删除出现错误");
} }

1
test05/test05.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 124 KiB