diff --git a/build.gradle.kts b/build.gradle.kts index efa7015..4520e18 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,7 +28,7 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-actuator") - implementation("org.mmga:make-minecraft-great-again-spring-boot-starter:0.0.5-20241219.044545-4") + implementation("org.mmga:make-minecraft-great-again-spring-boot-starter:0.0.5-20241219.110855-5") implementation("com.mybatis-flex:mybatis-flex-spring-boot-starter:1.10.2") annotationProcessor("com.mybatis-flex:mybatis-flex-processor:1.10.2") // https://mvnrepository.com/artifact/commons-codec/commons-codec diff --git a/src/main/java/org/blue/club/configuration/CustomAuthorizationHandler.java b/src/main/java/org/blue/club/configuration/CustomAuthorizationHandler.java index ca8791e..de42c2b 100644 --- a/src/main/java/org/blue/club/configuration/CustomAuthorizationHandler.java +++ b/src/main/java/org/blue/club/configuration/CustomAuthorizationHandler.java @@ -10,10 +10,13 @@ import org.mmga.spring.boot.starter.componet.JwtUtils; import org.mmga.spring.boot.starter.entities.Result; import org.mmga.spring.boot.starter.exception.AuthorizationException; import org.mmga.spring.boot.starter.utils.VoUtils; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import java.lang.annotation.Annotation; +import java.util.Arrays; import java.util.Optional; +import java.util.stream.LongStream; @Component @RequiredArgsConstructor @@ -26,13 +29,21 @@ public class CustomAuthorizationHandler implements AuthorizationHandler { public Optional auth(String token, Annotation ann) { if (ann instanceof Auth auth) { Optional i = jwtUtils.verifyToken(token); - if (i.isEmpty()) throw new AuthorizationException(Result.failed("token错误!")); + if (i.isEmpty()) throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "token错误!")); Long userId = i.get(); UserVo userVo = userDao.selectOneWithRelationsById(userId); - if (userVo == null) throw new AuthorizationException(Result.failed("用户不存在!")); + if (userVo == null) throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "用户不存在!")); User user = voUtils.vo2DtoSafe(userVo, User.class); - - return Optional.ofNullable(user); + long[] auths = auth.auths(); + LongStream authStream = Arrays.stream(auths); + boolean isAuthAccept = auths.length == 0 || auth.authType().equals(Auth.LogicType.ANY) ? authStream.anyMatch(user::isAuth) : authStream.allMatch(user::isAuth); + if (!isAuthAccept) throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "权限不足!")); + long[] permissions = auth.permissions(); + LongStream permissionStream = Arrays.stream(permissions); + boolean isPermissionAccept = permissions.length == 0 || auth.permissionType().equals(Auth.LogicType.ANY) ? permissionStream.anyMatch(user::hasPermission) : permissionStream.allMatch(user::hasPermission); + if (!isPermissionAccept) + throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "权限不足!")); + return Optional.of(user); } return Optional.empty(); } diff --git a/src/main/java/org/blue/club/controller/UserController.java b/src/main/java/org/blue/club/controller/UserController.java index 9327699..bc47c24 100644 --- a/src/main/java/org/blue/club/controller/UserController.java +++ b/src/main/java/org/blue/club/controller/UserController.java @@ -9,8 +9,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.blue.club.annotation.Auth; import org.blue.club.entities.dto.User; -import org.blue.club.entities.dto.req.LoginRequest; -import org.blue.club.entities.dto.req.RegisterRequest; +import org.blue.club.entities.dto.req.*; import org.blue.club.services.UserServices; import org.mmga.spring.boot.starter.annotation.AuthMapping; import org.mmga.spring.boot.starter.entities.PagerData; @@ -42,8 +41,8 @@ public class UserController { @GetMapping("/info") @Operation(description = "获取用户信息") - public Result getUserInfo(@RequestAttribute("user") int userId) { - return userServices.getUserInfo(userId); + public Result getUserInfo(@Auth User user) { + return Result.success(user); } @GetMapping("/all") @@ -56,32 +55,32 @@ public class UserController { @PutMapping("/rename") @Operation(description = "修改用户名") @Auth - public Result changeUsername(@RequestBody UserRenameVo renameVo, @Auth User user) { + public Result changeUsername(@RequestBody UserRenameRequest renameVo, @Auth User user) { return userServices.changeUsername(renameVo, user); } @PutMapping("/password") @Operation(description = "修改密码") - public Result changePassword(@RequestBody UserChangePasswordVo changePasswordVo, @Auth User user) { + public Result changePassword(@RequestBody UserChangePasswordRequest changePasswordVo, @Auth User user) { return userServices.changePassword(changePasswordVo, user); } @PutMapping("/auth") @Operation(description = "修改用户权限组") @Auth(permissions = {4}) - public Result changeAuth(@RequestBody UserChangeAuthVo userChangeAuthVo) { + public Result changeAuth(@RequestBody UserChangeAuthRequest userChangeAuthVo) { return userServices.changeAuth(userChangeAuthVo); } @PostMapping("/avatar") @Operation(description = "上传头像") public Result changeAvatar(MultipartFile file, @Auth User user) { - return userServices.changeAvatar(file, userId); + return userServices.changeAvatar(file, user); } @PutMapping("/avatar") @Operation(description = "修改用户头像") - public Result changeAvatar(@RequestParam("code") @Schema(description = "修改头像操作码,可以通过将图片文件上传至") String avatarOperationCode, @Auth User user) { + public Result changeAvatar(@RequestParam("code") @Schema(description = "修改头像操作码,可以通过将图片文件上传至([POST] /avatar)接口获取") String avatarOperationCode, @Auth User user) { return userServices.changeAvatar(avatarOperationCode, user); } diff --git a/src/main/java/org/blue/club/entities/dto/req/UserChangeAuthRequest.java b/src/main/java/org/blue/club/entities/dto/req/UserChangeAuthRequest.java new file mode 100644 index 0000000..8ef7c10 --- /dev/null +++ b/src/main/java/org/blue/club/entities/dto/req/UserChangeAuthRequest.java @@ -0,0 +1,8 @@ +package org.blue.club.entities.dto.req; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "用户修改权限组请求体") +public record UserChangeAuthRequest(@Schema(description = "被修改的用户ID") long id, + @Schema(description = "新的权限组ID") long authId) { +} diff --git a/src/main/java/org/blue/club/entities/dto/req/UserChangePasswordRequest.java b/src/main/java/org/blue/club/entities/dto/req/UserChangePasswordRequest.java new file mode 100644 index 0000000..9fa6687 --- /dev/null +++ b/src/main/java/org/blue/club/entities/dto/req/UserChangePasswordRequest.java @@ -0,0 +1,26 @@ +package org.blue.club.entities.dto.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.commons.codec.digest.DigestUtils; + +import java.util.Objects; + +@Schema(name = "用户修改密码请求体") +@Data +@AllArgsConstructor +public class UserChangePasswordRequest { + @Schema(description = "被修改的用户ID") + private final Long id; + @Schema(description = "修改前的密码MD5值", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String oldPassword; + @Schema(description = "修改后的密码MD5值") + private final String newPassword; + + public UserChangePasswordRequest sha1HexPassword() { + String oldPassword = Objects.isNull(this.oldPassword) ? null : DigestUtils.sha1Hex(this.oldPassword); + String newPassword = Objects.isNull(this.newPassword) ? null : DigestUtils.sha1Hex(this.newPassword); + return new UserChangePasswordRequest(this.id, oldPassword, newPassword); + } +} diff --git a/src/main/java/org/blue/club/entities/dto/req/UserRenameRequest.java b/src/main/java/org/blue/club/entities/dto/req/UserRenameRequest.java new file mode 100644 index 0000000..30328f4 --- /dev/null +++ b/src/main/java/org/blue/club/entities/dto/req/UserRenameRequest.java @@ -0,0 +1,18 @@ +package org.blue.club.entities.dto.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Schema(name = "用户修改用户名请求体") +@Data +@AllArgsConstructor +@RequiredArgsConstructor +public class UserRenameRequest { + @Schema(description = "用户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private Long id; + @Schema(description = "新用户名") + private final String newName; +} + diff --git a/src/main/java/org/blue/club/entities/vo/UserVo.java b/src/main/java/org/blue/club/entities/vo/UserVo.java index a0235ba..aab1e14 100644 --- a/src/main/java/org/blue/club/entities/vo/UserVo.java +++ b/src/main/java/org/blue/club/entities/vo/UserVo.java @@ -18,7 +18,7 @@ public class UserVo extends BaseVo { @VoIgnore private String password; @VoIgnore - private Integer auth; + private Long auth; @Nullable private String avatar; @RelationManyToOne(selfField = "auth", targetField = "id") diff --git a/src/main/java/org/blue/club/services/UserServices.java b/src/main/java/org/blue/club/services/UserServices.java index b4148b7..4e8fe79 100644 --- a/src/main/java/org/blue/club/services/UserServices.java +++ b/src/main/java/org/blue/club/services/UserServices.java @@ -1,19 +1,25 @@ package org.blue.club.services; +import com.mybatisflex.core.paginate.Page; import lombok.RequiredArgsConstructor; import org.blue.club.dao.UserDao; import org.blue.club.dao.redis.VerifyDao; import org.blue.club.entities.dto.User; -import org.blue.club.entities.dto.req.LoginRequest; -import org.blue.club.entities.dto.req.RegisterRequest; +import org.blue.club.entities.dto.req.*; import org.blue.club.entities.vo.UserVo; +import org.mmga.spring.boot.starter.entities.PagerData; import org.mmga.spring.boot.starter.entities.Result; +import org.mmga.spring.boot.starter.exception.AuthorizationException; +import org.mmga.spring.boot.starter.utils.RandomUtils; import org.mmga.spring.boot.starter.utils.VoUtils; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.util.List; import java.util.Optional; +import static com.mybatisflex.core.query.QueryMethods.select; import static org.blue.club.entities.vo.table.UserVoTableDef.USER_VO; @Service @@ -22,6 +28,7 @@ public class UserServices { private final UserDao userDao; private final VerifyDao verifyDao; private final VoUtils voUtils; + private final RandomUtils randomUtils; private boolean isWrongVerifyCode(String key, String code) { Optional targetCode = verifyDao.findById(key); @@ -49,7 +56,70 @@ public class UserServices { if (userDao.selectCountByCondition(USER_VO.NAME.eq(request.username())) > 0) { return Result.failed(HttpStatus.CONFLICT, "用户已存在"); } - userDao.insert() - return null; + UserVo userVo = new UserVo(); + userVo.setName(request.username()); + userVo.setPassword(request.sha1HexPassword()); + userVo.setAuth(request.auth()); + userDao.insert(userVo); + UserVo insertedUserData = userDao.selectOneWithRelationsById(userVo.getId()); + return Result.success(voUtils.vo2DtoSafe(insertedUserData, User.class)); + } + + public Result> getAllUserInfo(int num, int page) { + Page userVoPage = userDao.paginateWithRelations(page, num, select(USER_VO.ALL_COLUMNS).from(USER_VO)); + long totalRow = userVoPage.getTotalRow(); + List records = userVoPage.getRecords(); + return Result.success(new PagerData<>(totalRow, records.stream().map(e -> voUtils.vo2DtoSafe(e, User.class)).toList())); + } + + public Result changeUsername(UserRenameRequest renameVo, User user) { + Long id = renameVo.getId(); + Long operatorId = user.getId(); + if (id == null) id = operatorId; + if (!operatorId.equals(id) && !user.hasPermission(4L)) + throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "权限不足!")); + String s = renameVo.getNewName(); + if (userDao.selectCountByCondition(USER_VO.NAME.eq(s)) > 0) + return Result.failed(HttpStatus.CONFLICT, "用户名已存在!"); + UserVo userVo = new UserVo(); + userVo.setId(id); + userVo.setName(s); + userDao.update(userVo); + return Result.success(true); + } + + public Result changePassword(UserChangePasswordRequest changePasswordVo, User user) { + UserChangePasswordRequest shaHex = changePasswordVo.sha1HexPassword(); + String newPassword = shaHex.getNewPassword(); + String oldPassword = shaHex.getOldPassword(); + Long id = changePasswordVo.getId(); + UserVo newUserVo = new UserVo(); + newUserVo.setId(id); + newUserVo.setPassword(newPassword); + if (user.hasPermission(4L)) { + userDao.update(newUserVo); + return Result.success(true); + } + Long operatorUserId = user.getId(); + if (oldPassword == null || !id.equals(operatorUserId)) + throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "权限不足!")); + if (userDao.selectCountByCondition(USER_VO.ID.eq(operatorUserId)) <= 0) + return Result.failed(HttpStatus.NOT_FOUND, "旧密码不匹配"); + userDao.update(newUserVo); + return Result.success(true); + } + + public Result changeAuth(UserChangeAuthRequest userChangeAuthVo) { + UserVo userVo = new UserVo(); + userVo.setId(userChangeAuthVo.id()); + userVo.setAuth(userChangeAuthVo.authId()); + userDao.update(userVo); + return Result.success(true); + } + + public Result changeAvatar(MultipartFile file, User user) { + String s = randomUtils.generatorRandomFileName(32); + + } }