feat: 打造若依 (RuoYi) 规范的高品质 RBAC 用户权限体系,内置 admin/admin123 超级管理员与测试读者,支持登录注册、个人中心与用户管理
Build and Deploy Blog / deploy (push) Successful in 38s

This commit is contained in:
2026-10-22 17:22:47 +08:00
parent 9ad9923d61
commit 3bee3f5663
17 changed files with 1474 additions and 22 deletions
@@ -0,0 +1,98 @@
package com.example.blog.common;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class JwtUtils {
private static final String SECRET = "XiaoDong-RuoYi-Secret-Token-Key-2026-SuperSecureJWT!@#";
private static final long EXPIRATION_MS = 7L * 24 * 60 * 60 * 1000; // 7 天有效
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 生成 JWT Token
*/
public static String createToken(Long userId, String username, String nickname, String role) {
try {
long now = System.currentTimeMillis();
long exp = now + EXPIRATION_MS;
Map<String, Object> header = new HashMap<>();
header.put("alg", "HS256");
header.put("typ", "JWT");
Map<String, Object> payload = new HashMap<>();
payload.put("userId", userId);
payload.put("username", username);
payload.put("nickname", nickname);
payload.put("role", role);
payload.put("iat", now / 1000);
payload.put("exp", exp / 1000);
String encodedHeader = Base64.getUrlEncoder().withoutPadding()
.encodeToString(objectMapper.writeValueAsString(header).getBytes(StandardCharsets.UTF_8));
String encodedPayload = Base64.getUrlEncoder().withoutPadding()
.encodeToString(objectMapper.writeValueAsString(payload).getBytes(StandardCharsets.UTF_8));
String content = encodedHeader + "." + encodedPayload;
String signature = sign(content, SECRET);
return content + "." + signature;
} catch (Exception e) {
throw new RuntimeException("生成 Token 失败", e);
}
}
/**
* 解析并校验 Token,返回 Claims
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> parseToken(String token) {
if (token == null || token.trim().isEmpty()) {
return null;
}
if (token.startsWith("Bearer ")) {
token = token.substring(7).trim();
}
String[] parts = token.split("\\.");
if (parts.length != 3) {
return null;
}
try {
String content = parts[0] + "." + parts[1];
String expectedSignature = sign(content, SECRET);
if (!expectedSignature.equals(parts[2])) {
return null; // 签名不匹配
}
byte[] payloadBytes = Base64.getUrlDecoder().decode(parts[1]);
Map<String, Object> claims = objectMapper.readValue(payloadBytes, Map.class);
// 检查过期时间
Number exp = (Number) claims.get("exp");
if (exp != null && exp.longValue() * 1000 < System.currentTimeMillis()) {
return null; // Token 已过期
}
return claims;
} catch (Exception e) {
return null;
}
}
private static String sign(String data, String secret) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKey);
byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getUrlEncoder().withoutPadding().encodeToString(rawHmac);
}
}
@@ -0,0 +1,42 @@
package com.example.blog.common;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
public class SecurityUtils {
/**
* 生成随机盐值
*/
public static String generateSalt() {
return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
}
/**
* SHA-256 + 盐值加密密码 (若依风格安全加盐散列)
*/
public static String encryptPassword(String password, String salt) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
String salted = password + "{" + salt + "}";
byte[] bytes = md.digest(salted.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 算法未找到", e);
}
}
/**
* 校验密码
*/
public static boolean matchesPassword(String rawPassword, String encryptedPassword, String salt) {
String hash = encryptPassword(rawPassword, salt);
return hash.equalsIgnoreCase(encryptedPassword);
}
}
@@ -1,9 +1,12 @@
package com.example.blog.config;
import com.example.blog.common.SecurityUtils;
import com.example.blog.entity.Category;
import com.example.blog.entity.Post;
import com.example.blog.entity.SysUser;
import com.example.blog.repository.CategoryRepository;
import com.example.blog.repository.PostRepository;
import com.example.blog.repository.SysUserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@@ -15,14 +18,21 @@ public class DataInitializer implements CommandLineRunner {
private final PostRepository postRepository;
private final CategoryRepository categoryRepository;
private final SysUserRepository sysUserRepository;
public DataInitializer(PostRepository postRepository, CategoryRepository categoryRepository) {
public DataInitializer(PostRepository postRepository,
CategoryRepository categoryRepository,
SysUserRepository sysUserRepository) {
this.postRepository = postRepository;
this.categoryRepository = categoryRepository;
this.sysUserRepository = sysUserRepository;
}
@Override
public void run(String... args) {
// 0. 初始化系统内置若依风格用户 (admin / admin123 与测试读者)
initSystemUsers();
// 1. 初始化分类
if (categoryRepository.count() == 0) {
List<Category> categories = Arrays.asList(
@@ -113,4 +123,36 @@ public class DataInitializer implements CommandLineRunner {
postRepository.saveAll(existingPosts);
}
}
private void initSystemUsers() {
// 1. 初始化内置超级管理员 admin / admin123
if (!sysUserRepository.existsByUsername("admin")) {
SysUser admin = new SysUser();
admin.setUsername("admin");
String salt = SecurityUtils.generateSalt();
admin.setSalt(salt);
admin.setPassword(SecurityUtils.encryptPassword("admin123", salt));
admin.setNickname("小东 (管理员)");
admin.setEmail("j2570852518@gmail.com");
admin.setAvatar("👨‍💻");
admin.setRole("ADMIN");
admin.setStatus("0");
sysUserRepository.save(admin);
}
// 2. 初始化内置测试访客读者 ry_user / user123
if (!sysUserRepository.existsByUsername("ry_user")) {
SysUser visitor = new SysUser();
visitor.setUsername("ry_user");
String salt = SecurityUtils.generateSalt();
visitor.setSalt(salt);
visitor.setPassword(SecurityUtils.encryptPassword("user123", salt));
visitor.setNickname("若依读者");
visitor.setEmail("visitor@example.com");
visitor.setAvatar("🚀");
visitor.setRole("USER");
visitor.setStatus("0");
sysUserRepository.save(visitor);
}
}
}
@@ -0,0 +1,59 @@
package com.example.blog.controller;
import com.example.blog.common.JwtUtils;
import com.example.blog.common.Result;
import com.example.blog.dto.LoginBody;
import com.example.blog.dto.RegisterBody;
import com.example.blog.service.SysUserService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
@CrossOrigin(origins = "*")
public class AuthController {
private final SysUserService sysUserService;
public AuthController(SysUserService sysUserService) {
this.sysUserService = sysUserService;
}
@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody LoginBody loginBody, HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return Result.success(sysUserService.login(loginBody, ip));
}
@PostMapping("/register")
public Result<Void> register(@RequestBody RegisterBody registerBody) {
sysUserService.register(registerBody);
return Result.success();
}
@GetMapping("/info")
public Result<Map<String, Object>> getInfo(@RequestHeader(value = "Authorization", required = false) String authHeader) {
if (authHeader == null || authHeader.isEmpty()) {
return Result.error(401, "请先登录");
}
Map<String, Object> claims = JwtUtils.parseToken(authHeader);
if (claims == null) {
return Result.error(401, "登录凭证无效或已过期,请重新登录");
}
Number userIdNum = (Number) claims.get("userId");
if (userIdNum == null) {
return Result.error(401, "用户凭证异常");
}
return Result.success(sysUserService.getUserInfo(userIdNum.longValue()));
}
@PostMapping("/logout")
public Result<Void> logout() {
return Result.success();
}
}
@@ -1,11 +1,13 @@
package com.example.blog.controller;
import com.example.blog.common.JwtUtils;
import com.example.blog.common.Result;
import com.example.blog.entity.Comment;
import com.example.blog.service.CommentService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/posts/{postId}/comments")
@@ -24,7 +26,24 @@ public class CommentController {
}
@PostMapping
public Result<Comment> addComment(@PathVariable Long postId, @RequestBody Comment comment) {
public Result<Comment> addComment(@PathVariable Long postId,
@RequestBody Comment comment,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
if (authHeader != null && !authHeader.isEmpty()) {
Map<String, Object> claims = JwtUtils.parseToken(authHeader);
if (claims != null) {
String nickname = (String) claims.get("nickname");
String role = (String) claims.get("role");
if (nickname != null && !nickname.trim().isEmpty()) {
comment.setAuthor(nickname);
}
if (role != null) {
comment.setRole(role);
}
comment.setAvatar("ADMIN".equalsIgnoreCase(role) ? "👑" : "👨‍💻");
}
}
return Result.success(commentService.addComment(postId, comment));
}
}
@@ -1,11 +1,13 @@
package com.example.blog.controller;
import com.example.blog.common.JwtUtils;
import com.example.blog.common.Result;
import com.example.blog.entity.Post;
import com.example.blog.service.PostService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/posts")
@@ -18,6 +20,20 @@ public class PostController {
this.postService = postService;
}
private void checkAdmin(String authHeader) {
if (authHeader == null || authHeader.isEmpty()) {
throw new RuntimeException("无权操作:请使用管理员账号登录");
}
Map<String, Object> claims = JwtUtils.parseToken(authHeader);
if (claims == null) {
throw new RuntimeException("登录已失效,请重新登录");
}
String role = (String) claims.get("role");
if (!"ADMIN".equalsIgnoreCase(role)) {
throw new RuntimeException("无权操作:此功能仅限超级管理员使用");
}
}
@GetMapping
public Result<List<Post>> listPosts(
@RequestParam(required = false) String category,
@@ -31,17 +47,31 @@ public class PostController {
}
@PostMapping
public Result<Post> createPost(@RequestBody Post post) {
public Result<Post> createPost(@RequestBody Post post,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
checkAdmin(authHeader);
Map<String, Object> claims = JwtUtils.parseToken(authHeader);
if (claims != null) {
String nickname = (String) claims.get("nickname");
if (nickname != null && !nickname.trim().isEmpty()) {
post.setAuthor(nickname);
}
}
return Result.success(postService.createPost(post));
}
@PutMapping("/{id}")
public Result<Post> updatePost(@PathVariable Long id, @RequestBody Post post) {
public Result<Post> updatePost(@PathVariable Long id,
@RequestBody Post post,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
checkAdmin(authHeader);
return Result.success(postService.updatePost(id, post));
}
@DeleteMapping("/{id}")
public Result<Void> deletePost(@PathVariable Long id) {
public Result<Void> deletePost(@PathVariable Long id,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
checkAdmin(authHeader);
postService.deletePost(id);
return Result.success();
}
@@ -51,3 +81,4 @@ public class PostController {
return Result.success(postService.likePost(id));
}
}
@@ -0,0 +1,91 @@
package com.example.blog.controller;
import com.example.blog.common.JwtUtils;
import com.example.blog.common.Result;
import com.example.blog.dto.UpdateProfileBody;
import com.example.blog.dto.UpdatePwdBody;
import com.example.blog.entity.SysUser;
import com.example.blog.service.SysUserService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
public class SysUserController {
private final SysUserService sysUserService;
public SysUserController(SysUserService sysUserService) {
this.sysUserService = sysUserService;
}
private Map<String, Object> checkAuth(String authHeader) {
if (authHeader == null || authHeader.isEmpty()) {
throw new RuntimeException("请先登录后操作");
}
Map<String, Object> claims = JwtUtils.parseToken(authHeader);
if (claims == null) {
throw new RuntimeException("登录凭证已失效,请重新登录");
}
return claims;
}
private void checkAdmin(String authHeader) {
Map<String, Object> claims = checkAuth(authHeader);
String role = (String) claims.get("role");
if (!"ADMIN".equalsIgnoreCase(role)) {
throw new RuntimeException("无权操作,此功能仅限超级管理员使用");
}
}
@GetMapping
public Result<List<SysUser>> listUsers(@RequestHeader(value = "Authorization", required = false) String authHeader) {
checkAdmin(authHeader);
return Result.success(sysUserService.listUsers());
}
@PostMapping
public Result<SysUser> createUser(@RequestHeader(value = "Authorization", required = false) String authHeader,
@RequestBody SysUser user) {
checkAdmin(authHeader);
return Result.success(sysUserService.createUser(user));
}
@PutMapping("/{id}/status")
public Result<Void> changeStatus(@RequestHeader(value = "Authorization", required = false) String authHeader,
@PathVariable Long id,
@RequestParam String status) {
checkAdmin(authHeader);
sysUserService.changeStatus(id, status);
return Result.success();
}
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@RequestHeader(value = "Authorization", required = false) String authHeader,
@PathVariable Long id) {
checkAdmin(authHeader);
sysUserService.deleteUser(id);
return Result.success();
}
@PutMapping("/profile")
public Result<Void> updateProfile(@RequestHeader(value = "Authorization", required = false) String authHeader,
@RequestBody UpdateProfileBody body) {
Map<String, Object> claims = checkAuth(authHeader);
Number userId = (Number) claims.get("userId");
sysUserService.updateProfile(userId.longValue(), body);
return Result.success();
}
@PutMapping("/password")
public Result<Void> updatePassword(@RequestHeader(value = "Authorization", required = false) String authHeader,
@RequestBody UpdatePwdBody body) {
Map<String, Object> claims = checkAuth(authHeader);
Number userId = (Number) claims.get("userId");
sysUserService.updatePassword(userId.longValue(), body);
return Result.success();
}
}
@@ -0,0 +1,11 @@
package com.example.blog.dto;
public class LoginBody {
private String username;
private String password;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
@@ -0,0 +1,17 @@
package com.example.blog.dto;
public class RegisterBody {
private String username;
private String password;
private String nickname;
private String email;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
@@ -0,0 +1,14 @@
package com.example.blog.dto;
public class UpdateProfileBody {
private String nickname;
private String email;
private String avatar;
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getAvatar() { return avatar; }
public void setAvatar(String avatar) { this.avatar = avatar; }
}
@@ -0,0 +1,11 @@
package com.example.blog.dto;
public class UpdatePwdBody {
private String oldPassword;
private String newPassword;
public String getOldPassword() { return oldPassword; }
public void setOldPassword(String oldPassword) { this.oldPassword = oldPassword; }
public String getNewPassword() { return newPassword; }
public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
}
@@ -17,6 +17,12 @@ public class Comment {
@Column(nullable = false, length = 50)
private String author = "访客";
@Column(length = 255)
private String avatar = "👨‍💻";
@Column(length = 20)
private String role = "USER";
@Column(nullable = false, columnDefinition = "TEXT")
private String content;
@@ -43,6 +49,10 @@ public class Comment {
public void setPostId(Long postId) { this.postId = postId; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public String getAvatar() { return avatar; }
public void setAvatar(String avatar) { this.avatar = avatar; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public LocalDateTime getCreatedAt() { return createdAt; }
@@ -0,0 +1,114 @@
package com.example.blog.entity;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "sys_user")
public class SysUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false, length = 128)
private String password;
@Column(length = 50)
private String nickname;
@Column(length = 100)
private String email;
@Column(length = 255)
private String avatar;
@Column(nullable = false, length = 20)
private String role = "USER"; // ADMIN, USER
@Column(nullable = false, length = 1)
private String status = "0"; // 0: 正常, 1: 停用
@Column(length = 64)
private String salt;
@Column(name = "login_ip", length = 50)
private String loginIp;
@Column(name = "login_date")
private LocalDateTime loginDate;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
public void prePersist() {
if (createdAt == null) createdAt = LocalDateTime.now();
if (updatedAt == null) updatedAt = LocalDateTime.now();
if (role == null) role = "USER";
if (status == null) status = "0";
if (nickname == null || nickname.trim().isEmpty()) nickname = username;
if (avatar == null) avatar = "👨‍💻";
}
@PreUpdate
public void preUpdate() {
updatedAt = LocalDateTime.now();
}
public SysUser() {}
public SysUser(String username, String password, String nickname, String email, String role) {
this.username = username;
this.password = password;
this.nickname = nickname;
this.email = email;
this.role = role;
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getAvatar() { return avatar; }
public void setAvatar(String avatar) { this.avatar = avatar; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getSalt() { return salt; }
public void setSalt(String salt) { this.salt = salt; }
public String getLoginIp() { return loginIp; }
public void setLoginIp(String loginIp) { this.loginIp = loginIp; }
public LocalDateTime getLoginDate() { return loginDate; }
public void setLoginDate(LocalDateTime loginDate) { this.loginDate = loginDate; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}
@@ -0,0 +1,13 @@
package com.example.blog.repository;
import com.example.blog.entity.SysUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface SysUserRepository extends JpaRepository<SysUser, Long> {
Optional<SysUser> findByUsername(String username);
boolean existsByUsername(String username);
}
@@ -0,0 +1,31 @@
package com.example.blog.service;
import com.example.blog.dto.LoginBody;
import com.example.blog.dto.RegisterBody;
import com.example.blog.dto.UpdateProfileBody;
import com.example.blog.dto.UpdatePwdBody;
import com.example.blog.entity.SysUser;
import java.util.List;
import java.util.Map;
public interface SysUserService {
Map<String, Object> login(LoginBody loginBody, String ip);
void register(RegisterBody registerBody);
Map<String, Object> getUserInfo(Long userId);
List<SysUser> listUsers();
void changeStatus(Long userId, String status);
void updateProfile(Long userId, UpdateProfileBody body);
void updatePassword(Long userId, UpdatePwdBody body);
void deleteUser(Long userId);
SysUser createUser(SysUser user);
}
@@ -0,0 +1,226 @@
package com.example.blog.service.impl;
import com.example.blog.common.JwtUtils;
import com.example.blog.common.SecurityUtils;
import com.example.blog.dto.LoginBody;
import com.example.blog.dto.RegisterBody;
import com.example.blog.dto.UpdateProfileBody;
import com.example.blog.dto.UpdatePwdBody;
import com.example.blog.entity.SysUser;
import com.example.blog.repository.SysUserRepository;
import com.example.blog.service.SysUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
@Service
public class SysUserServiceImpl implements SysUserService {
private final SysUserRepository sysUserRepository;
public SysUserServiceImpl(SysUserRepository sysUserRepository) {
this.sysUserRepository = sysUserRepository;
}
@Override
@Transactional
public Map<String, Object> login(LoginBody loginBody, String ip) {
if (loginBody.getUsername() == null || loginBody.getUsername().trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (loginBody.getPassword() == null || loginBody.getPassword().trim().isEmpty()) {
throw new IllegalArgumentException("密码不能为空");
}
SysUser user = sysUserRepository.findByUsername(loginBody.getUsername().trim())
.orElseThrow(() -> new RuntimeException("用户名或密码错误"));
if ("1".equals(user.getStatus())) {
throw new RuntimeException("对不起,该账号已被停用,请联系管理员");
}
if (!SecurityUtils.matchesPassword(loginBody.getPassword(), user.getPassword(), user.getSalt())) {
throw new RuntimeException("用户名或密码错误");
}
// 更新登录信息
user.setLoginIp(ip != null ? ip : "127.0.0.1");
user.setLoginDate(LocalDateTime.now());
sysUserRepository.save(user);
// 生成 RuoYi 风格 Token 与鉴权负载
String token = JwtUtils.createToken(user.getId(), user.getUsername(), user.getNickname(), user.getRole());
Map<String, Object> result = new HashMap<>();
result.put("token", token);
Map<String, Object> safeUser = new HashMap<>();
safeUser.put("id", user.getId());
safeUser.put("username", user.getUsername());
safeUser.put("nickname", user.getNickname());
safeUser.put("email", user.getEmail());
safeUser.put("avatar", user.getAvatar());
safeUser.put("role", user.getRole());
safeUser.put("status", user.getStatus());
safeUser.put("loginDate", user.getLoginDate());
result.put("user", safeUser);
// 角色与权限标识 (若依标准格式)
result.put("roles", Collections.singletonList(user.getRole()));
if ("ADMIN".equalsIgnoreCase(user.getRole())) {
result.put("permissions", Collections.singletonList("*:*:*"));
} else {
result.put("permissions", Arrays.asList("post:view", "post:like", "comment:add"));
}
return result;
}
@Override
@Transactional
public void register(RegisterBody registerBody) {
String username = registerBody.getUsername() != null ? registerBody.getUsername().trim() : "";
String password = registerBody.getPassword() != null ? registerBody.getPassword().trim() : "";
if (username.length() < 3 || username.length() > 20) {
throw new IllegalArgumentException("用户名长度必须在 3 到 20 个字符之间");
}
if (password.length() < 5 || password.length() > 30) {
throw new IllegalArgumentException("密码长度必须在 5 到 30 个字符之间");
}
if (sysUserRepository.existsByUsername(username)) {
throw new RuntimeException("该用户名已被注册,请尝试其他用户名");
}
String salt = SecurityUtils.generateSalt();
String encryptedPassword = SecurityUtils.encryptPassword(password, salt);
SysUser user = new SysUser();
user.setUsername(username);
user.setPassword(encryptedPassword);
user.setSalt(salt);
user.setNickname(registerBody.getNickname() != null && !registerBody.getNickname().trim().isEmpty() ? registerBody.getNickname().trim() : username);
user.setEmail(registerBody.getEmail() != null ? registerBody.getEmail().trim() : "");
user.setAvatar("👨‍💻");
user.setRole("USER");
user.setStatus("0");
sysUserRepository.save(user);
}
@Override
@Transactional(readOnly = true)
public Map<String, Object> getUserInfo(Long userId) {
SysUser user = sysUserRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
Map<String, Object> result = new HashMap<>();
Map<String, Object> safeUser = new HashMap<>();
safeUser.put("id", user.getId());
safeUser.put("username", user.getUsername());
safeUser.put("nickname", user.getNickname());
safeUser.put("email", user.getEmail());
safeUser.put("avatar", user.getAvatar());
safeUser.put("role", user.getRole());
safeUser.put("status", user.getStatus());
safeUser.put("loginDate", user.getLoginDate());
safeUser.put("createdAt", user.getCreatedAt());
result.put("user", safeUser);
result.put("roles", Collections.singletonList(user.getRole()));
if ("ADMIN".equalsIgnoreCase(user.getRole())) {
result.put("permissions", Collections.singletonList("*:*:*"));
} else {
result.put("permissions", Arrays.asList("post:view", "post:like", "comment:add"));
}
return result;
}
@Override
@Transactional(readOnly = true)
public List<SysUser> listUsers() {
List<SysUser> users = sysUserRepository.findAll();
// 清除敏感字段
for (SysUser u : users) {
u.setPassword(null);
u.setSalt(null);
}
return users;
}
@Override
@Transactional
public void changeStatus(Long userId, String status) {
SysUser user = sysUserRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
if ("admin".equalsIgnoreCase(user.getUsername())) {
throw new RuntimeException("不允许停用内置超级管理员账号");
}
user.setStatus("1".equals(status) ? "1" : "0");
sysUserRepository.save(user);
}
@Override
@Transactional
public void updateProfile(Long userId, UpdateProfileBody body) {
SysUser user = sysUserRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
if (body.getNickname() != null && !body.getNickname().trim().isEmpty()) {
user.setNickname(body.getNickname().trim());
}
if (body.getEmail() != null) {
user.setEmail(body.getEmail().trim());
}
if (body.getAvatar() != null && !body.getAvatar().trim().isEmpty()) {
user.setAvatar(body.getAvatar().trim());
}
sysUserRepository.save(user);
}
@Override
@Transactional
public void updatePassword(Long userId, UpdatePwdBody body) {
SysUser user = sysUserRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
if (!SecurityUtils.matchesPassword(body.getOldPassword(), user.getPassword(), user.getSalt())) {
throw new RuntimeException("原密码错误,修改失败");
}
if (body.getNewPassword() == null || body.getNewPassword().length() < 5) {
throw new IllegalArgumentException("新密码长度不能少于 5 位");
}
String newSalt = SecurityUtils.generateSalt();
String newHash = SecurityUtils.encryptPassword(body.getNewPassword(), newSalt);
user.setSalt(newSalt);
user.setPassword(newHash);
sysUserRepository.save(user);
}
@Override
@Transactional
public void deleteUser(Long userId) {
SysUser user = sysUserRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
if ("admin".equalsIgnoreCase(user.getUsername())) {
throw new RuntimeException("不允许删除内置超级管理员账号");
}
sysUserRepository.deleteById(userId);
}
@Override
@Transactional
public SysUser createUser(SysUser user) {
if (sysUserRepository.existsByUsername(user.getUsername())) {
throw new RuntimeException("用户名已存在");
}
String salt = SecurityUtils.generateSalt();
user.setSalt(salt);
user.setPassword(SecurityUtils.encryptPassword(user.getPassword(), salt));
return sysUserRepository.save(user);
}
}