JWT工具类

JwtUtils

package com.leyou.auth.utils;

import com.leyou.auth.entity.UserInfo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;

import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * @author: li
 * @create: 2018-10-23
 **/
public class JwtUtils {
    /**
     * 私钥加密token
     *
     * @param userInfo      载荷中的数据
     * @param privateKey    私钥
     * @param expireMinutes 过期时间,单位秒
     * @return
     * @throws Exception
     */
    public static String generateToken(UserInfo userInfo, PrivateKey privateKey, int expireMinutes) throws Exception {
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, userInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername())
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param userInfo      载荷中的数据
     * @param privateKey    私钥字节数组
     * @param expireMinutes 过期时间,单位秒
     * @return
     * @throws Exception
     */
    public static String generateToken(UserInfo userInfo, byte[] privateKey, int expireMinutes) throws Exception {
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, userInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername())
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
                .signWith(SignatureAlgorithm.RS256, RsaUtils.getPrivateKey(privateKey))
                .compact();
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥字节数组
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, byte[] publicKey) throws Exception {
        return Jwts.parser().setSigningKey(RsaUtils.getPublicKey(publicKey))
                .parseClaimsJws(token);
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static UserInfo getInfoFromToken(String token, PublicKey publicKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return new UserInfo(
                ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME))
        );
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static UserInfo getInfoFromToken(String token, byte[] publicKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return new UserInfo(
                ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME))
        );
    }
}

RsaUtils

package com.leyou.auth.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Created by ace on 2018/10/23.
 *
 * @author li
 */
public class RsaUtils {
    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生成rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    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);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }
}

 

### JWT 工具类实现示例 以下是基于 Java 的 JWT 工具类实现示例,该工具类支持创建和解析 JSON Web Token (JWT),并提供了签名验证功能以防止篡改[^2]。 ```java import io.jsonwebtoken.*; import java.util.Date; import java.util.Map; public class JwtUtils { private static final String SECRET_KEY = "your_secret_key"; // 替换为实际使用的密钥 /** * 创建 JWT * * @param claims 自定义载荷数据 * @param ttlMillis 过期时间(毫秒) * @return JWT 字符串 */ public static String createJwt(Map<String, Object> claims, long ttlMillis) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); return Jwts.builder() .setClaims(claims) // 设置自定义载荷 .setIssuedAt(now) // 设置签发时间 .setExpiration(new Date(nowMillis + ttlMillis)) // 设置过期时间 .signWith(signatureAlgorithm, SECRET_KEY.getBytes()) // 使用 HMAC 签名算法和密钥进行签名 .compact(); } /** * 解析 JWT 并返回其中的 Claims 数据 * * @param jwtToken 待解析的 JWT 字符串 * @return Claims 对象 * @throws ExpiredJwtException 如果令牌已过期则抛出异常 * @throws UnsupportedJwtException 如果不支持当前令牌类型则抛出异常 * @throws MalformedJwtException 如果令牌格式错误则抛出异常 * @throws SignatureException 如果签名验证失败则抛出异常 */ public static Claims parseJwt(String jwtToken) throws JwtException { return Jwts.parserBuilder() .setSigningKey(SECRET_KEY.getBytes()) .build() .parseClaimsJws(jwtToken) .getBody(); } } ``` 上述代码实现了两个核心方法: 1. **`createJwt` 方法**:用于生成带有指定有效时间和自定义载荷的 JWT。通过 `SignatureAlgorithm.HS256` 和预设的密钥对生成的令牌进行签名,从而确保其不可被随意篡改。 2. **`parseJwt` 方法**:用于解析传入的 JWT,并提取其中的 Claims 数据。如果令牌已经过期、格式非法或者签名验证失败,则会分别抛出对应的异常[^3]。 --- ### 防止 JWT 被篡改的关键机制 为了防止 JWT 被恶意修改或伪造,在生成 JWT 时会对整个头部和载荷部分计算哈希值,并将其作为签名附加到最终的字符串中。当接收方收到此令牌后,可以通过相同的密钥重新计算签名并与原始签名对比来验证其真实性。 例如,在上面的 `parseJwt` 方法中,调用了 `.setSigningKey(SECRET_KEY.getBytes())` 来设置解码所需的密钥。只有持有相同密钥的一方才能够成功完成校验过程;任何试图更改负载内容的行为都会破坏原有的签名结构,进而导致验证失败。 --- ### 测试 JWT 的有效期 下面展示了一个简单的单元测试用例,演示如何利用前面提到的工具类检测某个特定时间段内的 token 是否仍然可用: ```java @Test public void testJwtValidityPeriod() throws Exception { Map<String, Object> claims = Map.of("username", "testUser"); // 创建一个仅持续三秒钟的有效令牌 String shortLivedJwt = JwtUtils.createJwt(claims, 3_000L); Thread.sleep(4_000); // 让线程休眠超过设定时限 try { JwtUtils.parseJwt(shortLivedJwt); fail("Expected an exception due to expired token."); } catch (ExpiredJwtException e) { System.out.println("Caught expected expiration error: " + e.getMessage()); } } ``` 在此例子中,我们故意让程序等待超出预期存活周期后再尝试解读刚才生产的短命凭证。不出所料的话,应该触发相应的超时期限警告消息。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值