From 6c58598a8eeb3cccfe6df73c2020c8f17ac80a89 Mon Sep 17 00:00:00 2001 From: q Date: Mon, 11 Aug 2025 13:23:30 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/qaiu/lz/common/util/JwtUtil.java | 185 ++++++++++++++++++ .../cn/qaiu/lz/common/util/PasswordUtil.java | 90 +++++++++ 2 files changed, 275 insertions(+) create mode 100644 web-service/src/main/java/cn/qaiu/lz/common/util/JwtUtil.java create mode 100644 web-service/src/main/java/cn/qaiu/lz/common/util/PasswordUtil.java diff --git a/web-service/src/main/java/cn/qaiu/lz/common/util/JwtUtil.java b/web-service/src/main/java/cn/qaiu/lz/common/util/JwtUtil.java new file mode 100644 index 0000000..f1ef675 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/common/util/JwtUtil.java @@ -0,0 +1,185 @@ +package cn.qaiu.lz.common.util; + +import cn.qaiu.lz.web.model.SysUser; +import io.vertx.core.json.JsonObject; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Base64; +import java.util.Date; + +/** + * JWT工具类,用于生成和验证JWT token + */ +public class JwtUtil { + + private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000; // token过期时间,24小时 + private static final String SECRET_KEY = "netdisk-fast-download-jwt-secret-key"; // 密钥 + private static final String ALGORITHM = "HmacSHA256"; + + /** + * 生成JWT token + * + * @param user 用户信息 + * @return JWT token + */ + public static String generateToken(SysUser user) { + long expireTime = getExpireTime(); + + // Header + JsonObject header = new JsonObject() + .put("alg", "HS256") + .put("typ", "JWT"); + + // Payload + JsonObject payload = new JsonObject() + .put("id", user.getId()) + .put("username", user.getUsername()) + .put("role", user.getRole()) + .put("exp", expireTime) + .put("iat", System.currentTimeMillis()) + .put("iss", "netdisk-fast-download"); + + // Base64 encode header and payload + String encodedHeader = Base64.getUrlEncoder().withoutPadding().encodeToString(header.encode().getBytes(StandardCharsets.UTF_8)); + String encodedPayload = Base64.getUrlEncoder().withoutPadding().encodeToString(payload.encode().getBytes(StandardCharsets.UTF_8)); + + // Create signature + String signature = hmacSha256(encodedHeader + "." + encodedPayload, SECRET_KEY); + + // Combine to form JWT + return encodedHeader + "." + encodedPayload + "." + signature; + } + + /** + * 使用HMAC-SHA256算法生成签名 + * + * @param data 要签名的数据 + * @param key 密钥 + * @return 签名 + */ + private static String hmacSha256(String data, String key) { + try { + Mac sha256Hmac = Mac.getInstance(ALGORITHM); + SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM); + sha256Hmac.init(secretKey); + byte[] signedBytes = sha256Hmac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + return Base64.getUrlEncoder().withoutPadding().encodeToString(signedBytes); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new RuntimeException("Error creating HMAC SHA256 signature", e); + } + } + + /** + * 验证JWT token + * + * @param token JWT token + * @return 如果token有效返回true,否则返回false + */ + public static boolean validateToken(String token) { + try { + String[] parts = token.split("\\."); + if (parts.length != 3) { + return false; + } + + String encodedHeader = parts[0]; + String encodedPayload = parts[1]; + String signature = parts[2]; + + // 验证签名 + String expectedSignature = hmacSha256(encodedHeader + "." + encodedPayload, SECRET_KEY); + if (!expectedSignature.equals(signature)) { + return false; + } + + // 验证过期时间 + String payload = new String(Base64.getUrlDecoder().decode(encodedPayload), StandardCharsets.UTF_8); + JsonObject payloadJson = new JsonObject(payload); + long expTime = payloadJson.getLong("exp", 0L); + + return System.currentTimeMillis() < expTime; + } catch (Exception e) { + return false; + } + } + + /** + * 从token中获取用户ID + * + * @param token JWT token + * @return 用户ID + */ + public static String getUserIdFromToken(String token) { + String[] parts = token.split("\\."); + if (parts.length != 3) { + return null; + } + + // Base64解码 + String payload = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); + JsonObject jsonObject = new JsonObject(payload); + return jsonObject.getString("id"); + } + + /** + * 从token中获取用户名 + * + * @param token JWT token + * @return 用户名 + */ + public static String getUsernameFromToken(String token) { + String[] parts = token.split("\\."); + if (parts.length != 3) { + return null; + } + + // Base64解码 + String payload = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); + JsonObject jsonObject = new JsonObject(payload); + return jsonObject.getString("username"); + } + + /** + * 从token中获取用户角色 + * + * @param token JWT token + * @return 用户角色 + */ + public static String getRoleFromToken(String token) { + String[] parts = token.split("\\."); + if (parts.length != 3) { + return null; + } + + // Base64解码 + String payload = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); + JsonObject jsonObject = new JsonObject(payload); + return jsonObject.getString("role"); + } + + /** + * 获取过期时间 + * + * @return 过期时间戳 + */ + private static long getExpireTime() { + return System.currentTimeMillis() + EXPIRE_TIME; + } + + /** + * 将过期时间戳转换为LocalDateTime + * + * @param expireTime 过期时间戳 + * @return LocalDateTime + */ + public static LocalDateTime getExpireTimeAsLocalDateTime(long expireTime) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(expireTime), ZoneId.systemDefault()); + } +} diff --git a/web-service/src/main/java/cn/qaiu/lz/common/util/PasswordUtil.java b/web-service/src/main/java/cn/qaiu/lz/common/util/PasswordUtil.java new file mode 100644 index 0000000..6d2ae24 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/common/util/PasswordUtil.java @@ -0,0 +1,90 @@ +package cn.qaiu.lz.common.util; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Base64; + +/** + * 密码加密工具类 + * 使用SHA-256算法加盐进行密码加密和验证 + */ +public class PasswordUtil { + + private static final String ALGORITHM = "SHA-256"; + private static final int SALT_LENGTH = 16; // 盐的长度 + private static final String DELIMITER = ":"; // 用于分隔盐和哈希值的分隔符 + + /** + * 对密码进行加密 + * + * @param plainPassword 明文密码 + * @return 加密后的密码(格式:salt:hash) + */ + public static String hashPassword(String plainPassword) { + if (plainPassword == null || plainPassword.isEmpty()) { + throw new IllegalArgumentException("密码不能为空"); + } + + try { + // 生成随机盐 + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[SALT_LENGTH]; + random.nextBytes(salt); + + // 计算哈希值 + MessageDigest md = MessageDigest.getInstance(ALGORITHM); + md.update(salt); + byte[] hashedPassword = md.digest(plainPassword.getBytes(StandardCharsets.UTF_8)); + + // 将盐和哈希值编码为Base64并拼接 + String saltBase64 = Base64.getEncoder().encodeToString(salt); + String hashBase64 = Base64.getEncoder().encodeToString(hashedPassword); + + // 返回格式:salt:hash + return saltBase64 + DELIMITER + hashBase64; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("加密算法不可用", e); + } + } + + /** + * 验证密码是否正确 + * + * @param plainPassword 明文密码 + * @param hashedPassword 加密后的密码(格式:salt:hash) + * @return 如果密码匹配返回true,否则返回false + */ + public static boolean checkPassword(String plainPassword, String hashedPassword) { + if (plainPassword == null || hashedPassword == null || hashedPassword.isEmpty()) { + return false; + } + + try { + // 分割盐和哈希值 + String[] parts = hashedPassword.split(DELIMITER); + if (parts.length != 2) { + return false; + } + + String saltBase64 = parts[0]; + String hashBase64 = parts[1]; + + // 解码盐 + byte[] salt = Base64.getDecoder().decode(saltBase64); + + // 使用相同的盐计算哈希值 + MessageDigest md = MessageDigest.getInstance(ALGORITHM); + md.update(salt); + byte[] calculatedHash = md.digest(plainPassword.getBytes(StandardCharsets.UTF_8)); + String calculatedHashBase64 = Base64.getEncoder().encodeToString(calculatedHash); + + // 比较计算出的哈希值和存储的哈希值 + return hashBase64.equals(calculatedHashBase64); + } catch (Exception e) { + // 如果发生异常(例如格式不正确),返回false + return false; + } + } +}