对接企业微信
This commit is contained in:
13
src/main/java/com/rj/common/DictItemConstants.java
Normal file
13
src/main/java/com/rj/common/DictItemConstants.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.rj.common;
|
||||
|
||||
/**
|
||||
* dict_item 相关常量
|
||||
*/
|
||||
public final class DictItemConstants {
|
||||
|
||||
private DictItemConstants() {
|
||||
}
|
||||
|
||||
public static final String QIWEI_CONFIG = "qiwei_config";
|
||||
}
|
||||
|
||||
@@ -2,6 +2,15 @@ package com.rj.common;
|
||||
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* 密码工具类
|
||||
*
|
||||
@@ -15,6 +24,13 @@ public class PasswordUtil {
|
||||
*/
|
||||
public static final String DEFAULT_SALT = "rj_system_2025";
|
||||
|
||||
/**
|
||||
* 可逆加密标记,避免重复加密
|
||||
*/
|
||||
private static final String ENC_PREFIX = "ENC$";
|
||||
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
|
||||
/**
|
||||
* MD5加密密码
|
||||
*
|
||||
@@ -47,6 +63,79 @@ public class PasswordUtil {
|
||||
return encryptPassword(password, DEFAULT_SALT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可逆加密(AES-GCM),输出格式:ENC$base64(iv).base64(cipherText)
|
||||
* <p>
|
||||
* 说明:此方法用于需要可解密的配置值(例如企微配置),不要用于用户密码。
|
||||
*/
|
||||
public static String encryptReversibleWithDefaultSalt(String plainText) {
|
||||
if (plainText == null) {
|
||||
plainText = "";
|
||||
}
|
||||
if (isEncryptedReversible(plainText)) {
|
||||
return plainText;
|
||||
}
|
||||
try {
|
||||
byte[] iv = new byte[12]; // GCM recommended IV length
|
||||
SECURE_RANDOM.nextBytes(iv);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, deriveAesKey(DEFAULT_SALT), spec);
|
||||
|
||||
byte[] cipherBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
||||
String ivB64 = Base64.getEncoder().encodeToString(iv);
|
||||
String ctB64 = Base64.getEncoder().encodeToString(cipherBytes);
|
||||
return ENC_PREFIX + ivB64 + "." + ctB64;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("encrypt reversible failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 可逆解密(AES-GCM),支持未加密值直接原样返回。
|
||||
*/
|
||||
public static String decryptReversibleWithDefaultSalt(String encryptedOrPlain) {
|
||||
if (encryptedOrPlain == null) {
|
||||
return null;
|
||||
}
|
||||
if (!isEncryptedReversible(encryptedOrPlain)) {
|
||||
return encryptedOrPlain;
|
||||
}
|
||||
try {
|
||||
String payload = encryptedOrPlain.substring(ENC_PREFIX.length());
|
||||
int dot = payload.indexOf('.');
|
||||
if (dot <= 0 || dot >= payload.length() - 1) {
|
||||
throw new IllegalArgumentException("bad encrypted format");
|
||||
}
|
||||
byte[] iv = Base64.getDecoder().decode(payload.substring(0, dot));
|
||||
byte[] cipherBytes = Base64.getDecoder().decode(payload.substring(dot + 1));
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
|
||||
cipher.init(Cipher.DECRYPT_MODE, deriveAesKey(DEFAULT_SALT), spec);
|
||||
byte[] plain = cipher.doFinal(cipherBytes);
|
||||
return new String(plain, StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("decrypt reversible failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEncryptedReversible(String value) {
|
||||
return value != null && value.startsWith(ENC_PREFIX);
|
||||
}
|
||||
|
||||
private static SecretKey deriveAesKey(String salt) {
|
||||
try {
|
||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||
byte[] keyBytes = sha256.digest(String.valueOf(salt).getBytes(StandardCharsets.UTF_8));
|
||||
// 256-bit AES key
|
||||
return new SecretKeySpec(keyBytes, "AES");
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("derive key failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user