feat: user related logic refactor

This commit is contained in:
merlin
2025-11-27 17:42:31 +08:00
parent 23cb31d4fe
commit 0ea6e13064
28 changed files with 738 additions and 52 deletions

View File

@@ -0,0 +1,67 @@
package xin.merlin.myplayerbackend.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Component
public class AESUtil {
// 16 位密钥(必须 16/24/32 位)
private final String key;
private static final String ALGORITHM = "AES";
public AESUtil(@Value("${aes.key}") String key) {
this.key = key;
}
/** 加密 */
public String encrypt(String plainText) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("AES 加密失败", e);
}
}
/** 解密 */
public String decrypt(String cipherText) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decoded = Base64.getDecoder().decode(cipherText);
byte[] original = cipher.doFinal(decoded);
return new String(original, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("AES 解密失败", e);
}
}
public Map<String, String> decryptAndSplit(String cipherText) {
String plainText = this.decrypt(cipherText); // 先解密
String[] parts = plainText.split(":", 2); // 只分割一次,防止 email 里有 :
if (parts.length != 2) {
throw new IllegalArgumentException("解密数据格式不正确:" + plainText);
}
Map<String, String> result = new HashMap<>();
result.put("id", parts[0]);
result.put("email", parts[1]);
return result;
}
}

View File

@@ -3,9 +3,12 @@ package xin.merlin.myplayerbackend.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.config.security.JwtProperties;
import xin.merlin.myplayerbackend.entity.Account;
import javax.crypto.SecretKey;
@@ -13,6 +16,8 @@ import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.UUID;
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {
@@ -35,17 +40,28 @@ public class JwtUtil {
}
}
@PreDestroy
public void destroy() {
log.info("JWT 组件正在关闭...");
// 如有线程池、定时任务、IO 连接,在这里 shutdown/close
// 本例仅把引用置空,帮助 GC可选
this.jwtParser = null;
this.key = null;
log.info("JWT 组件已关闭");
}
/**
* 生成 JWT Token
*/
public String generateToken(String account, Integer id) {
public String generateToken(Account account) {
Date now = new Date();
Date expireDate = new Date(now.getTime() + jwtProperties.getExpire() * 1000L);
return Jwts.builder()
.subject(account)
.claim("id", id)
.claim("account", account)
.subject(account.getAccount())
.claim("id", account.getId())
.claim("account", account.getAccount())
.claim("character", account.getCharacter())
.id(UUID.randomUUID().toString())
.issuedAt(now)
.expiration(expireDate)
@@ -57,7 +73,7 @@ public class JwtUtil {
/**
* 解析 Token 获取 Claims
*/
public Claims getClaims(String token) {
private Claims getClaims(String token) {
try {
Jws<Claims> jws = jwtParser.parseSignedClaims(token);
System.out.println(jws.getPayload());
@@ -95,6 +111,16 @@ public class JwtUtil {
return claims.get("id", Integer.class);
}
// public Integer getCharacter(String token) {
// Claims claims = getClaims(token);
// return claims.get("character", Integer.class);
// }
public Boolean isAdmin(String token) {
Claims claims = getClaims(token);
return claims.get("character", Integer.class)==0;
}
// 自定义异常类
public static class TokenExpiredException extends RuntimeException {
public TokenExpiredException(String message, Throwable cause) {

View File

@@ -0,0 +1,19 @@
package xin.merlin.myplayerbackend.utils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class SHA256Util {
public static String sha256(String s) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] bytes = md.digest(s.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : bytes) sb.append(String.format("%02x", b));
return sb.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -15,10 +15,16 @@ public enum ResultCode {
NOT_FOUND("404", "资源不存在"),
SERVER_ERROR("500", "服务器内部错误"),
// 自定义业务错误码
//账户相关
ACCOUNT_EXIST("2001","账户已存在"),
ACCOUNT_EXIST("3001","账户已存在"),
ACCOUNT_NOT_INIT("3002","账户未初始化"),
ACCOUNT_PWD_ERROR("3003","账户密码错误"),
ACCOUNT_INFO_LOST("3004","账户信息丢失"),
ACCOUNT_ILLEGAL_CHANGE("3005","账户非法篡改"),
ACCOUNT_PERMISSION_DENY("3006","用户权限不足"),
//用户相关
USER_BANNED("4000","用户被封禁"),
@@ -28,7 +34,7 @@ public enum ResultCode {
USER_VERIFICATION_ERROR("4005","验证码不存在或错误"),
USER_SEND_TOO_FAST("4006","用户请求过快"),
USER_SEND_TOO_OFTEN("4007","请求次数过多,已被限制"),
ORDER_NOT_FOUND("4000", "订单不存在"),
USER_ILLEGAL_REQUEST("4008", "用户非法请求"),
//邮箱相关
MAIL_ACCOUNT_NOT_PROVIDED("4101","未提供验证码接受账户"),
@@ -36,7 +42,10 @@ public enum ResultCode {
MAIL_INFO_LOST("4103","验证信息丢失"),
MAIL_VERIFY_FAIL_TOO_MANY("4104","验证码错误过多,请重新申请验证码"),
MAIL_VERIFY_NOT_EXIST("4105","验证码元素丢失,请重新申请验证码"),
MAIL_VERIFY_CODE_ERROR("4106","验证码错误,请重新输入");
MAIL_VERIFY_CODE_ERROR("4106","验证码错误,请重新输入"),
//审核相关
AUDIT_NO_RECORD("4201","无审核记录条目");