新增工具类,优化jwt
This commit is contained in:
parent
c77960c777
commit
4eafe160da
6
pom.xml
6
pom.xml
@ -48,6 +48,7 @@
|
||||
<knife4j-spring-boot-starter.version>3.0.3</knife4j-spring-boot-starter.version>
|
||||
<!-- 阿里巴巴excel解析版本配置 -->
|
||||
<easyexcel.version>2.2.11</easyexcel.version>
|
||||
<bcprov-jdk15on.version>1.70</bcprov-jdk15on.version>
|
||||
<!-- xxl-job-core核心版本号 -->
|
||||
<xxl-job-core.version>2.3.0</xxl-job-core.version>
|
||||
<!-- 第三方工具库 -->
|
||||
@ -111,6 +112,11 @@
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>${bcprov-jdk15on.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
|
@ -62,6 +62,10 @@
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
|
@ -0,0 +1,139 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package cn.zyjblogs.common.utils.crypto;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.util.Base64;
|
||||
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
||||
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine.Mode;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithRandom;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
|
||||
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
public class Sm2Utils {
|
||||
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
|
||||
private static byte SM2_CIPHER_FIRST_BIT = 4;
|
||||
private static ECParameterSpec param;
|
||||
|
||||
private Sm2Utils() {
|
||||
}
|
||||
|
||||
public static String encrypt(String publicKey, String data) throws Exception {
|
||||
return encrypt(publicKey, data, true);
|
||||
}
|
||||
|
||||
public static String encrypt(String publicKey, String data, boolean isNew) throws Exception {
|
||||
BCECPublicKey bcecPublicKey = getPublicKey(publicKey);
|
||||
ECParameterSpec parameters = bcecPublicKey.getParameters();
|
||||
ECDomainParameters ecDomainParameters = new ECDomainParameters(parameters.getCurve(), parameters.getG(), parameters.getN());
|
||||
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
|
||||
SM2Engine.Mode model = isNew ? Mode.C1C3C2 : Mode.C1C2C3;
|
||||
SM2Engine sm2Engine = new SM2Engine(model);
|
||||
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
|
||||
byte[] in = data.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] bytes = sm2Engine.processBlock(in, 0, in.length);
|
||||
byte[] base64Bytes = Base64.getEncoder().encode(bytes);
|
||||
return new String(base64Bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decrypt(String privateKey, String data) throws Exception {
|
||||
return decrypt(privateKey, data, true);
|
||||
}
|
||||
|
||||
public static String decrypt(String privateKey, String data, boolean isNew) throws Exception {
|
||||
byte[] base64Decode = Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] finalByte = addBitIfNeed(base64Decode);
|
||||
BCECPrivateKey bcecPrivateKey = getPrivateKey(privateKey);
|
||||
ECParameterSpec parameters = bcecPrivateKey.getParameters();
|
||||
ECDomainParameters ecDomainParameters = new ECDomainParameters(parameters.getCurve(), parameters.getG(), parameters.getN());
|
||||
ECPrivateKeyParameters ecPublicKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(), ecDomainParameters);
|
||||
SM2Engine.Mode model = isNew ? Mode.C1C3C2 : Mode.C1C2C3;
|
||||
SM2Engine sm2Engine = new SM2Engine(model);
|
||||
sm2Engine.init(false, ecPublicKeyParameters);
|
||||
byte[] bytes = sm2Engine.processBlock(finalByte, 0, finalByte.length);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String sign(String privateKey, String data) throws Exception {
|
||||
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString());
|
||||
BCECPrivateKey bcecPrivateKey = getPrivateKey(privateKey);
|
||||
signature.initSign(bcecPrivateKey);
|
||||
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] sign = signature.sign();
|
||||
return Base64.getEncoder().encodeToString(sign);
|
||||
}
|
||||
|
||||
public static boolean verify(String publicKey, String data, String sign) throws Exception {
|
||||
byte[] decode = Base64.getDecoder().decode(sign.getBytes(StandardCharsets.UTF_8));
|
||||
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString());
|
||||
BCECPublicKey bcecPublicKey = getPublicKey(publicKey);
|
||||
signature.initVerify(bcecPublicKey);
|
||||
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||
return signature.verify(decode);
|
||||
}
|
||||
|
||||
public static void generateKey() throws Exception {
|
||||
ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
|
||||
kpg.initialize(sm2Spec, new SecureRandom());
|
||||
KeyPair keyPair = kpg.generateKeyPair();
|
||||
BCECPublicKey bcecPublicKey = (BCECPublicKey)keyPair.getPublic();
|
||||
String publicKey = Hex.toHexString(bcecPublicKey.getQ().getEncoded(false));
|
||||
System.out.println("公钥:\n" + publicKey);
|
||||
BCECPrivateKey bcecPrivateKey = (BCECPrivateKey)keyPair.getPrivate();
|
||||
String privateKey = bcecPrivateKey.getD().toString(16);
|
||||
System.out.println("私钥:\n" + privateKey);
|
||||
}
|
||||
|
||||
private static BCECPublicKey getPublicKey(String publicKey) throws Exception {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
||||
ECPoint ecPoint = param.getCurve().decodePoint(Hex.decode(publicKey));
|
||||
return (BCECPublicKey)keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, param));
|
||||
}
|
||||
|
||||
private static BCECPrivateKey getPrivateKey(String privateKey) throws Exception {
|
||||
BigInteger d = new BigInteger(privateKey, 16);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
||||
return (BCECPrivateKey)keyFactory.generatePrivate(new ECPrivateKeySpec(d, param));
|
||||
}
|
||||
|
||||
private static byte[] addBitIfNeed(byte[] base64Decode) {
|
||||
byte first = base64Decode[0];
|
||||
if (first == SM2_CIPHER_FIRST_BIT) {
|
||||
return base64Decode;
|
||||
} else {
|
||||
byte[] finalByte = new byte[base64Decode.length + 1];
|
||||
finalByte[0] = SM2_CIPHER_FIRST_BIT;
|
||||
System.arraycopy(base64Decode, 0, finalByte, 1, base64Decode.length);
|
||||
return finalByte;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
param = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH());
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package cn.zyjblogs.common.utils.crypto;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Security;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
public class Sm3Utils {
|
||||
public Sm3Utils() {
|
||||
}
|
||||
|
||||
public static String hash(String data) {
|
||||
byte[] hash = hash(data.getBytes(StandardCharsets.UTF_8));
|
||||
return Hex.toHexString(hash);
|
||||
}
|
||||
|
||||
public static byte[] hash(byte[] data) {
|
||||
SM3Digest digest = new SM3Digest();
|
||||
digest.update(data, 0, data.length);
|
||||
byte[] hash = new byte[digest.getDigestSize()];
|
||||
digest.doFinal(hash, 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static String hmac(String key, String data) {
|
||||
byte[] hmac = hmac(key.getBytes(StandardCharsets.UTF_8), data.getBytes(StandardCharsets.UTF_8));
|
||||
return Hex.toHexString(hmac);
|
||||
}
|
||||
|
||||
public static byte[] hmac(byte[] key, byte[] data) {
|
||||
KeyParameter keyParameter = new KeyParameter(key);
|
||||
SM3Digest digest = new SM3Digest();
|
||||
HMac mac = new HMac(digest);
|
||||
mac.init(keyParameter);
|
||||
mac.update(data, 0, data.length);
|
||||
byte[] result = new byte[mac.getMacSize()];
|
||||
mac.doFinal(result, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean verify(String data, String hash) {
|
||||
String srcHash = hash(data);
|
||||
return hash.equals(srcHash);
|
||||
}
|
||||
|
||||
public static boolean verify(String key, String data, String hmac) {
|
||||
String srcHmac = hmac(key, data);
|
||||
return hmac.equals(srcHmac);
|
||||
}
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package cn.zyjblogs.common.utils.crypto;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.Key;
|
||||
import java.security.Security;
|
||||
import java.util.Base64;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
public class Sm4Utils {
|
||||
public static final String ALGORITHM_NAME = "SM4";
|
||||
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
|
||||
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
|
||||
|
||||
private Sm4Utils() {
|
||||
}
|
||||
|
||||
public static String encryptCbcBase64(String key, String iv, String cipherText) throws Exception {
|
||||
return encryptCbcBase64(key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8), cipherText.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static String decryptCbcBase64(String key, String iv, String cipherText) throws Exception {
|
||||
return decryptCbcBase64(key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8), cipherText.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static String encryptCbcBase64(byte[] key, byte[] iv, byte[] cipherText) throws Exception {
|
||||
byte[] bytes = encryptCbc(key, iv, cipherText);
|
||||
byte[] base64Encode = Base64.getEncoder().encode(bytes);
|
||||
return new String(base64Encode, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptCbcBase64(byte[] key, byte[] iv, byte[] cipherText) throws Exception {
|
||||
byte[] base64Decode = Base64.getDecoder().decode(cipherText);
|
||||
byte[] bytes = decryptCbc(key, iv, base64Decode);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static byte[] encryptCbc(byte[] key, byte[] iv, byte[] cipherText) throws Exception {
|
||||
Cipher cipher = generateCbcCipher(1, key, iv);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
public static byte[] decryptCbc(byte[] key, byte[] iv, byte[] cipherText) throws Exception {
|
||||
Cipher cipher = generateCbcCipher(2, key, iv);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
public static String encryptEcbBase64(String key, String cipherText) throws Exception {
|
||||
return encryptEcbBase64(key.getBytes(StandardCharsets.UTF_8), cipherText.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static String decryptEcbBase64(String key, String cipherText) throws Exception {
|
||||
return decryptEcbBase64(key.getBytes(StandardCharsets.UTF_8), cipherText.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static String encryptEcbBase64(byte[] key, byte[] cipherText) throws Exception {
|
||||
byte[] bytes = encryptEcb(key, cipherText);
|
||||
byte[] base64Encode = Base64.getEncoder().encode(bytes);
|
||||
return new String(base64Encode, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptEcbBase64(byte[] key, byte[] cipherText) throws Exception {
|
||||
byte[] base64Decode = Base64.getDecoder().decode(cipherText);
|
||||
byte[] bytes = decryptEcb(key, base64Decode);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static byte[] encryptEcb(byte[] key, byte[] cipherText) throws Exception {
|
||||
Cipher cipher = generateEcbCipher(1, key);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
public static byte[] decryptEcb(byte[] key, byte[] cipherText) throws Exception {
|
||||
Cipher cipher = generateEcbCipher(2, key);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
private static Cipher generateCbcCipher(int mode, byte[] key, byte[] iv) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "BC");
|
||||
Key sm4Key = new SecretKeySpec(key, "SM4");
|
||||
AlgorithmParameters params = AlgorithmParameters.getInstance("SM4");
|
||||
params.init(iv);
|
||||
cipher.init(mode, sm4Key, params);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
private static Cipher generateEcbCipher(int mode, byte[] key) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding", "BC");
|
||||
Key sm4Key = new SecretKeySpec(key, "SM4");
|
||||
cipher.init(mode, sm4Key);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package cn.zyjblogs.common.utils.jwt;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Clock;
|
||||
import io.jsonwebtoken.CompressionCodec;
|
||||
import io.jsonwebtoken.CompressionCodecResolver;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.Jwt;
|
||||
import io.jsonwebtoken.JwtParser;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.PrematureJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.impl.DefaultClaims;
|
||||
import io.jsonwebtoken.impl.DefaultClock;
|
||||
import io.jsonwebtoken.impl.DefaultHeader;
|
||||
import io.jsonwebtoken.impl.DefaultJws;
|
||||
import io.jsonwebtoken.impl.DefaultJwsHeader;
|
||||
import io.jsonwebtoken.impl.DefaultJwt;
|
||||
import io.jsonwebtoken.impl.DefaultJwtParser;
|
||||
import io.jsonwebtoken.impl.TextCodec;
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
|
||||
import io.jsonwebtoken.impl.crypto.JwtSignatureValidator;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Objects;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import java.security.Key;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class JwtParserUtils extends DefaultJwtParser {
|
||||
private long allowedClockSkewMillis = 0L;
|
||||
private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver();
|
||||
private byte[] keyBytes;
|
||||
private Key key;
|
||||
private SigningKeyResolver signingKeyResolver;
|
||||
private Clock clock;
|
||||
private boolean checkExpired;
|
||||
|
||||
public JwtParserUtils(boolean checkExpired) {
|
||||
this.clock = DefaultClock.INSTANCE;
|
||||
this.checkExpired = checkExpired;
|
||||
}
|
||||
|
||||
public Jwt parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException {
|
||||
Assert.hasText(jwt, "JWT String argument cannot be null or empty.");
|
||||
String base64UrlEncodedHeader = null;
|
||||
String base64UrlEncodedPayload = null;
|
||||
String base64UrlEncodedDigest = null;
|
||||
int delimiterCount = 0;
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
char[] var7 = jwt.toCharArray();
|
||||
int var8 = var7.length;
|
||||
|
||||
for(int var9 = 0; var9 < var8; ++var9) {
|
||||
char c = var7[var9];
|
||||
if (c == '.') {
|
||||
CharSequence tokenSeq = Strings.clean(sb);
|
||||
String token = tokenSeq != null ? tokenSeq.toString() : null;
|
||||
if (delimiterCount == 0) {
|
||||
base64UrlEncodedHeader = token;
|
||||
} else if (delimiterCount == 1) {
|
||||
base64UrlEncodedPayload = token;
|
||||
}
|
||||
|
||||
++delimiterCount;
|
||||
sb.setLength(0);
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (delimiterCount != 2) {
|
||||
String msg = "JWT strings must contain exactly 2 period characters. Found: " + delimiterCount;
|
||||
throw new MalformedJwtException(msg);
|
||||
} else {
|
||||
if (sb.length() > 0) {
|
||||
base64UrlEncodedDigest = sb.toString();
|
||||
}
|
||||
|
||||
if (base64UrlEncodedPayload == null) {
|
||||
throw new MalformedJwtException("JWT string '" + jwt + "' is missing a body/payload.");
|
||||
} else {
|
||||
Header header = null;
|
||||
CompressionCodec compressionCodec = null;
|
||||
String payload;
|
||||
if (base64UrlEncodedHeader != null) {
|
||||
payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader);
|
||||
Map<String, Object> m = this.readValue(payload);
|
||||
if (base64UrlEncodedDigest != null) {
|
||||
header = new DefaultJwsHeader(m);
|
||||
} else {
|
||||
header = new DefaultHeader(m);
|
||||
}
|
||||
|
||||
compressionCodec = this.compressionCodecResolver.resolveCompressionCodec((Header)header);
|
||||
}
|
||||
|
||||
if (compressionCodec != null) {
|
||||
byte[] decompressed = compressionCodec.decompress(TextCodec.BASE64URL.decode(base64UrlEncodedPayload));
|
||||
payload = new String(decompressed, Strings.UTF_8);
|
||||
} else {
|
||||
payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedPayload);
|
||||
}
|
||||
|
||||
Claims claims = null;
|
||||
if (payload.charAt(0) == '{' && payload.charAt(payload.length() - 1) == '}') {
|
||||
Map<String, Object> claimsMap = this.readValue(payload);
|
||||
claims = new DefaultClaims(claimsMap);
|
||||
}
|
||||
|
||||
if (base64UrlEncodedDigest != null) {
|
||||
JwsHeader jwsHeader = (JwsHeader)header;
|
||||
SignatureAlgorithm algorithm = null;
|
||||
String object;
|
||||
if (header != null) {
|
||||
object = jwsHeader.getAlgorithm();
|
||||
if (Strings.hasText(object)) {
|
||||
algorithm = SignatureAlgorithm.forName(object);
|
||||
}
|
||||
}
|
||||
|
||||
if (algorithm == null || algorithm == SignatureAlgorithm.NONE) {
|
||||
object = "JWT string has a digest/signature, but the header does not reference a valid signature algorithm.";
|
||||
throw new MalformedJwtException(object);
|
||||
}
|
||||
|
||||
if (this.key != null && this.keyBytes != null) {
|
||||
throw new IllegalStateException("A key object and key bytes cannot both be specified. Choose either.");
|
||||
}
|
||||
|
||||
if ((this.key != null || this.keyBytes != null) && this.signingKeyResolver != null) {
|
||||
object = this.key != null ? "a key object" : "key bytes";
|
||||
throw new IllegalStateException("A signing key resolver and " + object + " cannot both be specified. Choose either.");
|
||||
}
|
||||
|
||||
Key key = this.key;
|
||||
if (key == null) {
|
||||
byte[] keyBytes = this.keyBytes;
|
||||
if (Objects.isEmpty(keyBytes) && this.signingKeyResolver != null) {
|
||||
if (claims != null) {
|
||||
key = this.signingKeyResolver.resolveSigningKey(jwsHeader, claims);
|
||||
} else {
|
||||
key = this.signingKeyResolver.resolveSigningKey(jwsHeader, payload);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Objects.isEmpty(keyBytes)) {
|
||||
Assert.isTrue(algorithm.isHmac(), "Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance.");
|
||||
key = new SecretKeySpec(keyBytes, algorithm.getJcaName());
|
||||
}
|
||||
}
|
||||
|
||||
Assert.notNull(key, "A signing key must be specified if the specified JWT is digitally signed.");
|
||||
String jwtWithoutSignature = base64UrlEncodedHeader + "." + base64UrlEncodedPayload;
|
||||
|
||||
JwtSignatureValidator validator;
|
||||
try {
|
||||
validator = this.createSignatureValidator(algorithm, (Key)key);
|
||||
} catch (IllegalArgumentException var26) {
|
||||
String algName = algorithm.getValue();
|
||||
String msg = "The parsed JWT indicates it was signed with the " + algName + " signature algorithm, but the specified signing key of type " + key.getClass().getName() + " may not be used to validate " + algName + " signatures. Because the specified signing key reflects a specific and expected algorithm, and the JWT does not reflect this algorithm, it is likely that the JWT was not expected and therefore should not be trusted. Another possibility is that the parser was configured with the incorrect signing key, but this cannot be assumed for security reasons.";
|
||||
throw new UnsupportedJwtException(msg, var26);
|
||||
}
|
||||
|
||||
if (!validator.isValid(jwtWithoutSignature, base64UrlEncodedDigest)) {
|
||||
String msg = "JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.";
|
||||
throw new SignatureException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
boolean allowSkew = this.allowedClockSkewMillis > 0L;
|
||||
if (claims != null) {
|
||||
Date now = this.clock.now();
|
||||
long nowTime = now.getTime();
|
||||
Date exp = claims.getExpiration();
|
||||
String nbfVal;
|
||||
SimpleDateFormat sdf;
|
||||
if (exp != null && this.checkExpired) {
|
||||
long maxTime = nowTime - this.allowedClockSkewMillis;
|
||||
Date max = allowSkew ? new Date(maxTime) : now;
|
||||
if (max.after(exp)) {
|
||||
sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
String expVal = sdf.format(exp);
|
||||
nbfVal = sdf.format(now);
|
||||
long differenceMillis = maxTime - exp.getTime();
|
||||
String msg = "JWT expired at " + expVal + ". Current time: " + nbfVal + ", a difference of " + differenceMillis + " milliseconds. Allowed clock skew: " + this.allowedClockSkewMillis + " milliseconds.";
|
||||
throw new ExpiredJwtException((Header)header, claims, msg);
|
||||
}
|
||||
}
|
||||
|
||||
Date nbf = claims.getNotBefore();
|
||||
if (nbf != null) {
|
||||
long minTime = nowTime + this.allowedClockSkewMillis;
|
||||
Date min = allowSkew ? new Date(minTime) : now;
|
||||
if (min.before(nbf)) {
|
||||
sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
nbfVal = sdf.format(nbf);
|
||||
String nowVal = sdf.format(now);
|
||||
long differenceMillis = nbf.getTime() - minTime;
|
||||
String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal + ", a difference of " + differenceMillis + " milliseconds. Allowed clock skew: " + this.allowedClockSkewMillis + " milliseconds.";
|
||||
throw new PrematureJwtException((Header)header, claims, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object body = claims != null ? claims : payload;
|
||||
if (base64UrlEncodedDigest != null) {
|
||||
return new DefaultJws((JwsHeader)header, body, base64UrlEncodedDigest);
|
||||
} else {
|
||||
return new DefaultJwt((Header)header, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public JwtParser setSigningKey(Key key) {
|
||||
Assert.notNull(key, "signing key cannot be null.");
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package cn.zyjblogs.common.utils.rsa;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
|
||||
public class RsaUtils {
|
||||
private static final int DEFAULT_KEY_SIZE = 2048;
|
||||
|
||||
/**
|
||||
* 从文件中读取公钥
|
||||
*
|
||||
* @param filename 公钥保存路径
|
||||
* @return 公钥对象
|
||||
* @throws Exception
|
||||
*/
|
||||
public static PublicKey getPublicKey(String filename) throws Exception {
|
||||
byte[] bytes = readFile(filename);
|
||||
return getPublicKey(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取密钥
|
||||
*
|
||||
* @param filename 私钥保存路径
|
||||
* @return 私钥对象
|
||||
* @throws Exception
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(String filename) throws Exception {
|
||||
byte[] bytes = readFile(filename);
|
||||
return getPrivateKey(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公钥
|
||||
*
|
||||
* @param bytes 公钥的字节形式
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static PublicKey getPublicKey(byte[] bytes) throws Exception {
|
||||
bytes = Base64.getDecoder().decode(bytes);
|
||||
X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
return factory.generatePublic(spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取密钥
|
||||
*
|
||||
* @param bytes 私钥的字节形式
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException,
|
||||
InvalidKeySpecException {
|
||||
bytes = Base64.getDecoder().decode(bytes);
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
return factory.generatePrivate(spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据密文,生成rsa公钥和私钥,并写入指定文件
|
||||
*
|
||||
* @param publicKeyFilename 公钥文件绝对路径,比如:xxx/xxx/rsa_key.pub
|
||||
* @param privateKeyFilename 私钥文件绝对路径,比如:xxx/xxx/rsa_key
|
||||
* @param secret 生成密钥的密文
|
||||
*/
|
||||
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String
|
||||
secret, int keySize) throws Exception {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
SecureRandom secureRandom = new SecureRandom(secret.getBytes());
|
||||
keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
// 获取公钥并写出
|
||||
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
|
||||
publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
|
||||
writeFile(publicKeyFilename, publicKeyBytes);
|
||||
// 获取私钥并写出
|
||||
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
|
||||
privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
|
||||
writeFile(privateKeyFilename, privateKeyBytes);
|
||||
}
|
||||
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String
|
||||
secret) throws Exception {
|
||||
generateKey(publicKeyFilename,privateKeyFilename,secret,DEFAULT_KEY_SIZE);
|
||||
}
|
||||
|
||||
private static byte[] readFile(String fileName) throws Exception {
|
||||
return Files.readAllBytes(new File(fileName).toPath());
|
||||
}
|
||||
|
||||
private static void writeFile(String destPath, byte[] bytes) throws IOException {
|
||||
File dest = new File(destPath);
|
||||
File dir = dest.getParentFile();
|
||||
//判断目录是否存在,不在则新建
|
||||
if(!dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
//判断文件是否存在,不在则新建
|
||||
if (!dest.exists()) {
|
||||
dest.createNewFile();
|
||||
}
|
||||
Files.write(dest.toPath(), bytes);
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,41 @@ package cn.zyjblogs.oauth.config.security;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author zhuyijun
|
||||
*/
|
||||
//@Component("oauthAuthenticationProvider")
|
||||
//public class OauthAuthenticationProvider extends DaoAuthenticationProvider {
|
||||
//
|
||||
// @Override
|
||||
// protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
// super.additionalAuthenticationChecks(userDetails, authentication);
|
||||
// }
|
||||
//}
|
||||
@Component("oauthAuthenticationProvider")
|
||||
public class OauthAuthenticationProvider extends DaoAuthenticationProvider {
|
||||
|
||||
public OauthAuthenticationProvider(UserDetailsService userDetailsService){
|
||||
setUserDetailsService(userDetailsService);
|
||||
this.setPasswordEncoder(new BCryptPasswordEncoder());
|
||||
setForcePrincipalAsString(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
return super.authenticate(authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 认证
|
||||
* @param userDetails
|
||||
* @param authentication
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
super.additionalAuthenticationChecks(userDetails, authentication);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package cn.zyjblogs.oauth.config.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* Copyright (C), 2021, 北京同创永益科技发展有限公司
|
||||
*
|
||||
* @author zhuyijun
|
||||
* @version 3.0.0
|
||||
* @description
|
||||
* @date 2022/8/19 16:12
|
||||
*/
|
||||
@Configuration
|
||||
public class PasswordEncoderConfig {
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import org.springframework.security.oauth2.provider.client.JdbcClientDetailsServ
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Configuration
|
||||
@ -25,17 +26,15 @@ import javax.sql.DataSource;
|
||||
@RequiredArgsConstructor
|
||||
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
// private final OauthUserDetailsServiceImpl oauthUserDetailsService;
|
||||
// private final OauthAuthenticationProvider oauthAuthenticationProvider;
|
||||
private final OauthUserDetailsServiceImpl userDetailsService;
|
||||
private final OauthAuthenticationProvider oauthAuthenticationProvider;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
/**
|
||||
* 密码编码解码
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 认证管理器
|
||||
@ -50,12 +49,12 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
// auth.authenticationProvider(oauthAuthenticationProvider)
|
||||
// .userDetailsService(oauthUserDetailsService)
|
||||
// .passwordEncoder(passwordEncoder());
|
||||
// }
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.authenticationProvider(oauthAuthenticationProvider)
|
||||
.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(passwordEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
@ -69,7 +68,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
.authenticated()
|
||||
.and()
|
||||
//允许表单登录
|
||||
.formLogin();
|
||||
.formLogin()
|
||||
.and()
|
||||
.httpBasic();
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -40,7 +41,7 @@ public class OauthUserDetails implements UserDetails, Serializable {
|
||||
private Integer status;
|
||||
|
||||
private Integer deleted;
|
||||
private Collection<GrantedAuthority> authorities;
|
||||
private Collection<Role> authorities;
|
||||
private boolean accountNonExpired = true;
|
||||
private boolean accountNonLocked = true;
|
||||
private boolean credentialsNonExpired = true;
|
||||
@ -110,7 +111,7 @@ public class OauthUserDetails implements UserDetails, Serializable {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
public void setAuthorities(Collection<GrantedAuthority> authorities) {
|
||||
public void setAuthorities(Collection<Role> authorities) {
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
@ -132,7 +133,7 @@ public class OauthUserDetails implements UserDetails, Serializable {
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return authorities;
|
||||
return authorities == null ? Collections.emptyList():authorities;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,28 @@
|
||||
package cn.zyjblogs.oauth.server.user.po;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* Copyright (C), 2021, 北京同创永益科技发展有限公司
|
||||
*
|
||||
* @author zhuyijun
|
||||
* @version 3.0.0
|
||||
* @description
|
||||
* @date 2022/8/19 16:52
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class Role implements GrantedAuthority {
|
||||
private String id;
|
||||
private String name;
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ import java.util.List;
|
||||
/**
|
||||
* @author zhuyijun
|
||||
*/
|
||||
@Service("oauthUserDetailsService")
|
||||
@Service("userDetailsService")
|
||||
@RequiredArgsConstructor
|
||||
public class OauthUserDetailsServiceImpl implements UserDetailsService {
|
||||
private final UserService userService;
|
||||
|
Loading…
Reference in New Issue
Block a user