diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index ff0b88f3b3967eb759b38cd353e6e10471094353..de0d6c4330c75f6cdfa3c9f956e4febf7a4e85c2 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -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: diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenRsaConfig.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenRsaConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..ee5e059f38d3b4d979902614d5bb2c87e80a6745 --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenRsaConfig.java @@ -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)); + } +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/CustomSaJwtTemplate.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/CustomSaJwtTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..1a08d34673bcc267acf0c5c77deb4f6fbfea3d29 --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/CustomSaJwtTemplate.java @@ -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; + } + } + } + } +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/properties/SaTokenRsaProperties.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/properties/SaTokenRsaProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..89eb07965b194699f08c57762e578166313f199f --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/properties/SaTokenRsaProperties.java @@ -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; + +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/RSAUtil.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/RSAUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..fe14048711c96ee9b18480180fc4be07064a7c1e --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/RSAUtil.java @@ -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); + } +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 6dd284fb277b8c93bf6fb14b6619502f8311ddd1..63de6c61e22d61479070d63dca596bb0c7c0e894 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1,2 @@ org.dromara.common.satoken.config.SaTokenConfig +org.dromara.common.satoken.config.SaTokenRsaConfig