Pre Merge pull request !760 from tanqinhao/sa-token-jwt支持使用rs256生成token

This commit is contained in:
tanqinhao 2025-09-04 03:39:55 +00:00 committed by Gitee
commit d7d1b512ff
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 206 additions and 0 deletions

View File

@ -104,6 +104,11 @@ sa-token:
is-share: false
# jwt秘钥
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
rsa:
enabled: false
publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxRyMohAfHyH3GzUulXNUkaoPHOKF7X4JgixkW7GtmAQuMazR7ruF5QgpA+37qWAHaqGWNX2GoU1gAM4XUlw+g0K++4I6Vh759ZjpNn92nYfowOzsInhRLVFgojKhkCbRuf6py6i9Lvqf1cZ5BiRPoQP0vVrEnO8Xf9RgNdj6Hg92Pm/DRpxp1+EBPvs9fNWfgGAGd7L4r10qVCB9uLoEmt9/I6l2hhmAecmMbyn2BQwJqUthXC6lOhyDkjiWrG7Q3VrJ1bufl2ZG56ViHT8UQL/gSaNizuGecSPF7FYyslWx5FayhKTtIbtHsFgEfOuR+eVcGT9HeABrBiF69IM8bwIDAQAB
privateKey: MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQDFHIyiEB8fIfcbNS6Vc1SRqg8c4oXtfgmCLGRbsa2YBC4xrNHuu4XlCCkD7fupYAdqoZY1fYahTWAAzhdSXD6DQr77gjpWHvn1mOk2f3adh+jA7OwieFEtUWCiMqGQJtG5/qnLqL0u+p/VxnkGJE+hA/S9WsSc7xd/1GA12PoeD3Y+b8NGnGnX4QE++z181Z+AYAZ3svivXSpUIH24ugSa338jqXaGGYB5yYxvKfYFDAmpS2FcLqU6HIOSOJasbtDdWsnVu5+XZkbnpWIdPxRAv+BJo2LO4Z5xI8XsVjKyVbHkVrKEpO0hu0ewWAR865H55VwZP0d4AGsGIXr0gzxvAgMBAAECggEATfwqo9/vADlG8+vJn3V+yxbWXd49kKXiCy+XETQt80wMHrk6YdKted0NpTFd9Tg6FvURYd5lCqJ7TjY7wekfDn204/s1K2ecC5BRDK/HdTIVY2cZUdX6gAyHSATyKP9C0OyxrxL2roSYAlNBBK4PFyxigxxLV6+8w2V6HzPWWVega1inzqbafwMkC2DAFjtQqxHXHf8bo93GNCh/GfPKSzZnzeNSQo0JHOOu8VKyapsJe7enJqsyKWdVMUK2G/ikIebNqV+0y8aFSwUeoqx/FaBtF2fSNaUUqM3Zwo+oF5E7UdnI7FlFK+Q0LiMz0pGER88/p4rStcFCoAKIAG8boQKBgQDPrq9kN4MBqFOWRBXAuGbZQIh0VAInfXcdLPrIiIbxRGw8yOP/JTLqz+VhVuDzGoaKVxhRm0rG0P78q3ICyyKUOENrmtURvLVjSad1EH2PNyI6OOevk/BU2rUgsJkwMVBKawPsrmL8EEPGuaQHtS3KoLcS83UIYVbcwYKxjUyiHwKBgQDy+EcCbSdlTdtJXep6hDRB6AaLmFvr/aVhvI9jFDrkgm2U/VnHXyuFo+uJDCAFUniVGoYL3sNCcu8oLEYEI/kxbvy8F4+jsFeQ/lv7DUwzKJo8QcELlwvDntbNHd43a/LdFOcg4q+Y48zJenS9mrx/JyYEcVMTULdu/0oqBZY7sQKBgH3/LMlQJFrNFGkFxWy2cy86x45LqULsC2fX8g3XRob5S/FUr34Lw2hR2IfFfZP/c8BaNpbL8AHOqeQA35pyQvhJah3OQZYRNrqV5NJQvtw0Rr+r5mPPDO9uAJVkjot+X4fGhQsCgWsLMxNNbQaAFNfpySfmLAp5g4lKnU+6udNhAoGAIhQAoa59oA/Tee7aPGTej3/jqyCrg+YHlDXGRrdHq1U/2W3Pcc8/Y3ciSg6RTy3KC9+0fWW5LVDBinjrORlykiRm/lqvot3Q6l1CuWucDHDdcSYn1WBSHhXhOtO9nzhcz/VmlY414tPQnVG3ngBd/9eV1XmvlT30/HSCCnLOJ4ECf1Eq6JZ3uC/p2nG+Kpy7JrYL/TeIFlSZ0e6gaoGpjmzQasCnP4mgwLKwl8eYooBy0iqbN4Cpj73E+/FwirlTUS/QUoN1TLIMWYR+FEAYV1Pd6C6FgbDWp3aOdBY4zN17VHViIlc6Qoeh1Hw9b5wQ0usHGdNjJhBitiqzdfp3oX8=
# security配置
security:

View File

@ -0,0 +1,31 @@
package org.dromara.common.satoken.config;
import cn.dev33.satoken.jwt.SaJwtUtil;
import jakarta.annotation.PostConstruct;
import org.dromara.common.satoken.core.CustomSaJwtTemplate;
import org.dromara.common.satoken.properties.SaTokenRsaProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* sa-token RSA密钥自动配置
*
* @author T
*/
@AutoConfiguration
@EnableConfigurationProperties(SaTokenRsaProperties.class)
@ConditionalOnProperty(value = "sa-token.rsa.enabled", havingValue = "true")
public class SaTokenRsaConfig {
@Autowired
SaTokenRsaProperties properties;
/**
* 自定义 SaJwtUtil 生成 token 的算法
*/
@PostConstruct
public void setSaJwtTemplate() {
SaJwtUtil.setSaJwtTemplate(new CustomSaJwtTemplate(properties));
}
}

View File

@ -0,0 +1,107 @@
package org.dromara.common.satoken.core;
import cn.dev33.satoken.jwt.SaJwtTemplate;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTException;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import org.dromara.common.satoken.properties.SaTokenRsaProperties;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Objects;
/**
* 自定义 jwt 模版
*
* @author T
*/
public class CustomSaJwtTemplate extends SaJwtTemplate {
private PublicKey publicKey;
private PrivateKey privateKey;
public CustomSaJwtTemplate(SaTokenRsaProperties properties) {
this.publicKey = SecureUtil.rsa(null,properties.getPublicKey()).getPublicKey();
this.privateKey = SecureUtil.rsa(properties.getPrivateKey(),null).getPrivateKey();
}
@Override
public String generateToken(JWT jwt, String keyt) {
return jwt.setSigner(this.createSigner(privateKey)).sign();
}
public JWTSigner createSigner(Key key) {
if (SaFoxUtil.isEmpty(key)) {
throw new SaJwtException("请配置 RSA 密钥");
}
return JWTSignerUtil.rs256(key);
}
@Override
public JWT parseToken(String token, String loginType, String keyt, boolean isCheckTimeout) {
if (SaFoxUtil.isEmpty(keyt)) {
throw new SaJwtException("请配置 jwt 秘钥");
} else if (token == null) {
throw new SaJwtException("jwt 字符串不可为空");
} else {
JWT jwt;
try {
jwt = JWT.of(token);
} catch (JSONException | JWTException e) {
throw (new SaJwtException("jwt 解析失败:" + token, e)).setCode(30201);
}
JSONObject payloads = jwt.getPayloads();
boolean verify = jwt.setSigner(this.createSigner(publicKey)).verify();
if (!verify) {
throw (new SaJwtException("jwt 签名无效:" + token)).setCode(30202);
} else if (!Objects.equals(loginType, payloads.getStr("loginType"))) {
throw (new SaJwtException("jwt loginType 无效:" + token)).setCode(30203);
} else {
if (isCheckTimeout) {
Long effTime = payloads.getLong("eff", 0L);
if (effTime != -1L && (effTime == null || effTime < System.currentTimeMillis())) {
throw (new SaJwtException("jwt 已过期:" + token)).setCode(30204);
}
}
return jwt;
}
}
}
@Override
public long getTimeout(String token, String loginType, String keyt) {
if (token == null) {
return -2L;
} else {
JWT jwt;
try {
jwt = JWT.of(token);
} catch (JWTException var8) {
return -2L;
}
JSONObject payloads = jwt.getPayloads();
boolean verify = jwt.setSigner(this.createSigner(publicKey)).verify();
if (!verify) {
return -2L;
} else if (!Objects.equals(loginType, payloads.getStr("loginType"))) {
return -2L;
} else {
Long effTime = (Long) payloads.get("eff", Long.class);
if (effTime == -1L) {
return -1L;
} else {
return effTime != null && effTime >= System.currentTimeMillis() ? (effTime - System.currentTimeMillis()) / 1000L : -2L;
}
}
}
}
}

View File

@ -0,0 +1,30 @@
package org.dromara.common.satoken.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* sa-token RSA密钥配置类
*
* @author T
*/
@Data
@ConfigurationProperties(prefix = "sa-token.rsa")
public class SaTokenRsaProperties {
/**
* rsa开关
*/
private Boolean enabled;
/**
* 公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
}

View File

@ -0,0 +1,32 @@
package org.dromara.common.satoken.utils;
import cn.hutool.crypto.SecureUtil;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
/**
* RSA工具类
* 生成密钥对
*
* @author T
*/
public class RSAUtil {
public static void main(String[] args) throws Exception {
generateKeys();
}
public static void generateKeys() throws Exception {
KeyPair keyPair = SecureUtil.generateKeyPair("RSA", 2048);
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String publicKeyBase64String = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String privateKeyBase64String = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 保存到配置文件
System.out.println("sa-token.ras.publicKey: " + publicKeyBase64String);
System.out.println("sa-token.ras.privateKey: " + privateKeyBase64String);
}
}

View File

@ -1 +1,2 @@
org.dromara.common.satoken.config.SaTokenConfig
org.dromara.common.satoken.config.SaTokenRsaConfig