From a4f95e7315b6c698c3a981e58610f1bf943b79c5 Mon Sep 17 00:00:00 2001 From: ikmkj Date: Tue, 3 Mar 2026 21:09:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E8=A7=92=E8=89=B2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B9=B6=E5=AE=9E=E7=8E=B0=E6=9D=83=E9=99=90=E6=8E=A7?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在用户表中添加role字段并设置默认值为'USER' - 前端添加isAdmin getter判断用户角色 - 后端实现角色字段的VO映射和默认值设置 - 为关键接口添加@PreAuthorize权限控制 - 移除图片控制器中冗余的权限检查代码 --- .../controller/GroupingController.java | 4 ++ .../controller/ImageController.java | 69 +------------------ .../controller/MarkdownController.java | 4 ++ .../controller/TrashController.java | 6 +- .../controller/UserController.java | 2 + .../com/test/bijihoudaun/entity/UserVO.java | 3 + .../service/impl/UserServiceImpl.java | 1 + biji-qianduan/src/stores/user.js | 4 +- sql/all.sql | 1 + sql/mysql/all.sql | 4 +- 10 files changed, 29 insertions(+), 69 deletions(-) diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/GroupingController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/GroupingController.java index 7835faf..47d54a9 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/GroupingController.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/GroupingController.java @@ -10,6 +10,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -23,6 +24,7 @@ public class GroupingController { private GroupingService groupingService; @Operation(summary = "创建分组") + @PreAuthorize("hasRole('ADMIN')") @PostMapping public R createGrouping(@RequestBody Grouping grouping) { if (ObjectUtil.isNull(grouping.getParentId())) { @@ -47,6 +49,7 @@ public class GroupingController { } @Operation(summary = "更新分组名称") + @PreAuthorize("hasRole('ADMIN')") @PutMapping("/{id}") public R updateGrouping( @PathVariable String id, @@ -59,6 +62,7 @@ public class GroupingController { } @Operation(summary = "删除分组") + @PreAuthorize("hasRole('ADMIN')") @DeleteMapping("/{id}") public R deleteGrouping(@PathVariable String id) { Long idLong = Long.parseLong(id); diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java index 4ef8241..caa4b09 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java @@ -66,15 +66,9 @@ public class ImageController { } @Operation(summary = "根据id删除图片") + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{id}") public R deleteImage(@PathVariable Long id) { - if (!SecurityUtil.isUserAuthenticated()) { - return R.fail("请先登录"); - } - // 修复:添加权限验证,确保用户只能删除自己的图片 - if (!canModifyImage(id)) { - return R.fail("无权删除此图片"); - } boolean result = imageService.deleteImage(id); if (result) { return R.success(); @@ -138,15 +132,9 @@ public class ImageController { @Operation(summary = "根据url删除图片") + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/deleteByUrl") public R deleteImageByUrl(@RequestParam String url) { - if (!SecurityUtil.isUserAuthenticated()) { - return R.fail("请先登录"); - } - // 修复:添加权限验证 - if (!canModifyImageByUrl(url)) { - return R.fail("无权删除此图片"); - } boolean result = imageService.deleteImageByUrl(url); if (result) { return R.success(); @@ -156,17 +144,9 @@ public class ImageController { } @Operation(summary = "根据url批量删除图片") + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/batch") public R deleteImageByUrls(@RequestBody List urls) { - if (!SecurityUtil.isUserAuthenticated()) { - return R.fail("请先登录"); - } - // 修复:添加权限验证 - for (String url : urls) { - if (!canModifyImageByUrl(url)) { - return R.fail("无权删除部分图片"); - } - } boolean result = imageService.deleteImageByUrls(urls); if (result) { return R.success(); @@ -175,49 +155,6 @@ public class ImageController { } } - /** - * 检查当前用户是否有权限操作图片 - */ - private boolean canModifyImage(Long imageId) { - // 从数据库查询图片所属用户 - Image image = imageService.getById(imageId); - if (image == null) { - return false; - } - Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if (!(principal instanceof UserDetails)) { - return false; - } - String username = ((UserDetails) principal).getUsername(); - User user = userService.getOne(new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper().eq("username", username)); - if (user == null) { - return false; - } - return user.getId().equals(image.getUserId()); - } - - /** - * 检查当前用户是否有权限操作图片(通过URL) - */ - private boolean canModifyImageByUrl(String url) { - // 从数据库查询图片所属用户 - Image image = imageService.getOne(new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper().eq("stored_name", url)); - if (image == null) { - return false; - } - Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if (!(principal instanceof UserDetails)) { - return false; - } - String username = ((UserDetails) principal).getUsername(); - User user = userService.getOne(new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper().eq("username", username)); - if (user == null) { - return false; - } - return user.getId().equals(image.getUserId()); - } - - private String getContentTypeFromFileExtension(String fileName) { if (StrUtil.isBlank(fileName) || !StrUtil.contains(fileName, '.')) { return "application/octet-stream"; diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java index 495e444..5f4c9da 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java @@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.Date; @@ -55,6 +56,7 @@ public class MarkdownController { @Operation(summary = "更新Markdown文件") + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/updateMarkdown") public R updateMarkdown(@RequestBody MarkdownFile markdownFile) { MarkdownFile file = markdownFileService.updateMarkdownContent(markdownFile); @@ -69,6 +71,7 @@ public class MarkdownController { } @Operation(summary = "删除Markdown文件") + @PreAuthorize("hasRole('ADMIN')") @Parameters({ @Parameter(name = "id", description = "Markdown文件ID", required = true), }) @@ -95,6 +98,7 @@ public class MarkdownController { } @Operation(summary = "更新Markdown文件标题") + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{id}/title") public R updateMarkdownTitle( @PathVariable Long id, diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java index ff214d8..bb4fde8 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java @@ -7,6 +7,7 @@ import com.test.bijihoudaun.service.TrashService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -26,6 +27,7 @@ public class TrashController { } @PostMapping("/restore/{type}/{id}") + @PreAuthorize("hasRole('ADMIN')") @Operation(summary = "恢复项目") public R restoreItem(@PathVariable String type, @PathVariable String id) { trashService.restoreItem(id, type); @@ -33,6 +35,7 @@ public class TrashController { } @DeleteMapping("/permanently/{type}/{id}") + @PreAuthorize("hasRole('ADMIN')") @RequireCaptcha("永久删除") @Operation(summary = "永久删除项目") public R permanentlyDeleteItem(@PathVariable String type, @PathVariable String id) { @@ -41,10 +44,11 @@ public class TrashController { } @DeleteMapping("/clean") + @PreAuthorize("hasRole('ADMIN')") @RequireCaptcha("清空回收站") @Operation(summary = "清空回收站") public R cleanTrash() { trashService.cleanTrash(); return R.success(); } -} \ No newline at end of file +} diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/UserController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/UserController.java index afed605..61ba17e 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/UserController.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/UserController.java @@ -85,6 +85,8 @@ public class UserController { userInfo.put("id", String.valueOf(user.getId())); userInfo.put("username", user.getUsername()); userInfo.put("email", user.getEmail()); + String role = user.getRole(); + userInfo.put("role", (role != null && !role.isEmpty()) ? role : "USER"); result.put("userInfo", userInfo); return R.success(result); diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/UserVO.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/UserVO.java index c188d3b..7c2c13c 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/UserVO.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/UserVO.java @@ -21,6 +21,9 @@ public class UserVO implements Serializable { @Schema(description = "邮箱") private String email; + @Schema(description = "用户角色") + private String role; + @Schema(description = "用户创建时间") private Date createdAt; diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/UserServiceImpl.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/UserServiceImpl.java index 892a74d..787f918 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/UserServiceImpl.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/UserServiceImpl.java @@ -87,6 +87,7 @@ public class UserServiceImpl extends ServiceImpl implements Us user.setUsername(username); user.setPassword(encrypt); user.setEmail(email); + user.setRole("USER"); // 设置默认角色 userMapper.insert(user); return user; } diff --git a/biji-qianduan/src/stores/user.js b/biji-qianduan/src/stores/user.js index 311669a..a9250e0 100644 --- a/biji-qianduan/src/stores/user.js +++ b/biji-qianduan/src/stores/user.js @@ -30,6 +30,8 @@ export const useUserStore = defineStore('user', { }, getters: { isLoggedIn: (state) => !!state.token, + // 添加:判断是否为管理员 + isAdmin: (state) => state.userInfo?.role === 'ADMIN', }, persist: { enabled: true, @@ -40,4 +42,4 @@ export const useUserStore = defineStore('user', { } ], }, -}); \ No newline at end of file +}); diff --git a/sql/all.sql b/sql/all.sql index 5a357be..c1aadc4 100644 --- a/sql/all.sql +++ b/sql/all.sql @@ -168,6 +168,7 @@ CREATE TABLE "user" ( "username" TEXT NOT NULL, "password" TEXT NOT NULL, "email" TEXT, + "role" TEXT DEFAULT 'USER', "created_at" DATETIME DEFAULT CURRENT_TIMESTAMP, "updated_at" DATETIME DEFAULT CURRENT_TIMESTAMP, "token" TEXT, diff --git a/sql/mysql/all.sql b/sql/mysql/all.sql index 7516cec..7eacc2e 100644 --- a/sql/mysql/all.sql +++ b/sql/mysql/all.sql @@ -84,12 +84,14 @@ CREATE TABLE `user` ( `username` VARCHAR(50) NOT NULL, `password` VARCHAR(255) NOT NULL, `email` VARCHAR(100) DEFAULT NULL, + `role` VARCHAR(50) DEFAULT 'USER' COMMENT '用户角色:ADMIN-管理员,USER-普通用户', `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `token` VARCHAR(255) DEFAULT NULL, `token_enddata` DATETIME DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_username` (`username`) + UNIQUE KEY `uk_username` (`username`), + KEY `idx_user_role` (`role`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4; SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file