从0到1搭建前后端分离的脚手架框架之后端(四) JWT TOKEN 整合

整合JWT TOKEN

选择的jwt jar包为 jjwt, token采用RSA加密,来提升token的安全性.
首先我们来看一下RSA加密类( RsaHelper), 基本思路就是公钥私钥放到resources目录下,初始化的时候加载秘钥. 其中有一个内部类 RsaConfig对外暴露配置参数,设置了默认值和必填值

@Slf4j
@Component
@DependsOn("applicationContextHelper")
public class RsaHelper {
    private static final String KEY_ALGORITHM = "RSA";
    private static RsaConfig rsaConfig;
    public static  RSAPublicKey PUBLIC_KEY;
    public static RSAPrivateKey PRIVATE_KEY;
    @PostConstruct
    public void init() throws IOException, ClassNotFoundException {
        rsaConfig = ApplicationContextHelper.getBean(RsaConfig.class);
        PUBLIC_KEY = (RSAPublicKey) FileUtils.readObject(rsaConfig.getPublicKeyPath());
        PRIVATE_KEY = (RSAPrivateKey) FileUtils.readObject(rsaConfig.getPrivateKeyPath());

    }
    public static String encrypt(String data) {
        try {
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, PUBLIC_KEY);
            byte[] dataBytes = cipher.doFinal(data.getBytes(rsaConfig.getChatSet()));
            return Base64.getEncoder().encodeToString(dataBytes);
        } catch (Exception e) {
            log.warn("加密数据错误", e);
            throw new RsaException(GlobalCode.RSA_ERROR, "加密数据错误");
        }

    }
    public static String decrypt(String data) {
        try {
            byte[] bytes = Base64.getDecoder().decode(data);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, PRIVATE_KEY);
            return new String(cipher.doFinal(bytes),rsaConfig.getChatSet());
        } catch (Exception e) {
            log.warn("解密数据错误", e);
            throw new RsaException(GlobalCode.RSA_ERROR, "解密数据错误");
        }

    }

    public static void generateRsaKeyFile(String publicKeyName, String privateKeyName) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGenerator.initialize(rsaConfig.getKeySize());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        FileUtils.writeObject(keyPair.getPublic(), publicKeyName);
        FileUtils.writeObject(keyPair.getPrivate(),privateKeyName);
    }

    @Data
    @Validated
    @Configuration
    @ConfigurationProperties(prefix = "platform.rsa")
    public static class RsaConfig {
        @NotNull
        private String privateKeyPath;
        @NotNull
        private String publicKeyPath;
        // 秘钥长度
        private Integer keySize = 2048;
        //签名算法
        private String signatureAlgorithm = "SHA256withRSA";
        private String chatSet = "UTF-8";
    }

}

配置参数

platform:
  jwt:
    expiration: 40
  rsa:
    private-key-path: /static/rsa/rsa-private-key.dat
    public-key-path: /static/rsa/rsa-public-key.dat
    key-size: 3072

RsaHelper测试

@SpringBootTest
class RsaHelperTest {

    @Test
    void getKeyPair() throws Exception {
        RsaHelper.generateRsaKeyFile("D:/rsa-public-key.dat", "D:/rsa-private-key.dat");
    }

    @Test
    void testSign() {
        String test = RsaHelper.encrypt("asdfasdfasdfdsf");
        System.out.println(test);
        System.out.println(RsaHelper.decrypt(test));
        System.out.println(RsaHelper.decrypt("xbBilrMKSEJ+DqOCgh+vhb1AW6xxTKG2J+DEMNwrHy91vBy3ck+QbNuotDlAuHHTcqBGKvHMJQUDGoL9oILtft4Di9L1OxnnU8eOsV4p1WGBuW3X9zyYgiMEicqXBF/mXO/3qPuS245Gi3JiS9dm+R0no1TGwBL6IPiwPvbAtSfMaZGpR9Qn76sV5d1zGD58Ts9+c4dr5mqxGlnNILhiAvRMScYyKH0MZSX1E5HTf74Q0VwwUDoc2sJ6tZHopjMoRhJ4yKnpunsbpWJZTZ+4RsDjP4age8L9VGPdg2FzPSkXskOOFhmF+fqeUhXClEb6efvPCi2FF7+7aNVErCNHixp6p3kr5DXG4shyVMI7N/Za0UrjyXebXb3dDwBVcNrgE04pzA2Mn4QMqG9jjdz+AZWX3hr0ldOc21aQ6a7eWeiOPWOSkWt8sjoH8vYauEKE+MULd+os8H28Q7QUgGOFSr3WyOuNRVZqJG+/SoaTQl1Y+vhD4kV0QyQcHup4/jY5"));

    }
}

RSA加解密完成后我们开始整合jwt, JwtTokenHelper设计思路和RSA差不多,这里需要说明一下 parseToken()方法感觉设计的不是太合理,当token过期时直接返回null值, 返回类型为 Pair<String, String>,这里返回两个值 userIdusername,暂时先这样设计,以后感觉不好用在重构.

@Component
@DependsOn({"applicationContextHelper", "rsaHelper"})
public class JwtTokenHelper {
    private static JwtTokenConfig jwtTokenConfig;
    @PostConstruct
    public void init() {
        jwtTokenConfig = ApplicationContextHelper.getBean(JwtTokenConfig.class);
    }

    public static String generateToken(String tokenId, String tokenName) {
        return Jwts.builder()
                .claim(jwtTokenConfig.getTokenId(), tokenId)
                .claim(jwtTokenConfig.getTokenName(), tokenName)
                .setSubject(tokenName)
                .setExpiration(DateUtils.localDateTimeToDateConverter(
                    LocalDateTime.now()
                    .plusMinutes(jwtTokenConfig.getExpiration().toMinutes())
                ))
                .signWith(SignatureAlgorithm.RS256, RsaHelper.PRIVATE_KEY)
                .setIssuedAt(new Date())
                .compact();
    }

    public static Pair<String, String> parseToken(String token) {
        Jws<Claims> claims = Jwts.parser().setSigningKey(RsaHelper.PUBLIC_KEY).parseClaimsJws(token);
        if (claims.getBody().getExpiration().before(new Date())) {
            return null;
        }
        return Pair.of(Objects.toString(claims.getBody().get(jwtTokenConfig.getTokenId())),
                Objects.toString(claims.getBody().get(jwtTokenConfig.getTokenName())));
    }

    public static String getHeader() {
        return jwtTokenConfig.getHeader();
    }

    public static String getPrefix() {
        return jwtTokenConfig.getPrefix();
    }

    @Data
    @Configuration
    @ConfigurationProperties(prefix = "platform.jwt")
    public static class JwtTokenConfig {
        private String header = "Authorization";
        private String prefix = "Bearer ";
        private String tokenId = "userId";
        private String tokenName = "username";
        @DurationUnit(ChronoUnit.MINUTES)
        private Duration expiration = Duration.ofMinutes(120);
    }
}

测试类

@SpringBootTest
class JwtTokenHelperTest {

    @Test
    void generateToken() {
        String token = JwtTokenHelper.generateToken("test", "username");
        Pair<String, String > pair = JwtTokenHelper.parseToken(token);
        Assertions.assertNotNull(pair);
        Assertions.assertEquals(pair.getLeft(), "test");
        Assertions.assertEquals(pair.getRight(), "username");

    }
}

jwt token算是整合完了,明天开始进行鉴权和认证操作,打算写两个版本一个基于 spring security一个自己手动写鉴权和认证操作,感兴趣的可以关注一下我的github.

项目源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值