JWT非对称加密公私钥加解密可设置过期时间【Spring安全框架的JwtHelper】【jsonwebtoken.JJWT的Jwts】

引入

最近要开发一个谔码者管理平台,登录模块的技术选型我选择了JWT
Jwt可以保证放在客户端的登录信息不被篡改。
为了更加安全,加密算法我采用RSA非对称加密。即使用私钥生成Jwt,公钥校验Jwt。

问题

一开始想用spring的security框架,因为其中包含了JWT、Bcrypt以及其他内容,而且使用JwtHelper类,可以很方便的用私钥生成JWT,公钥解密。
但是搜遍全网,没找到JwtHelper设置Jwt过期时间的方法。
有个博主说,可以在载荷中手动增加iat、exp数据。但是经过测试,还是不行。
最终还是决定使用Jwts进行加密解密

JWT的加密解密

代码中提供Jwts和JwtHelper两种方式的加解密

第一步:生成密钥

通过keyTool(java提供的证书管理工具)生成
只要安装了JDK,就有这个工具。可以在cmd界面,输入keytool,检测是否有该工具。
若出现:

‘keytool’ 不是内部或外部命令,也不是可运行的程序或批处理文件。

'keytool' 不是内部或外部命令,也不是可运行的程序或批处理文件
可以进入jdk安装目录,bin文件夹下,就有keytool.exe文件。
此时,在地址栏输入cmd,回车,即可在此处打开cmd窗口。
资源管理器keytool文件
执行以下命令:

// 以下指令为一行,设置多行是方便阅读
keytool -genkeypair 
-alias 密钥别名 
-keyalg 使用的算法 
-keypass 密钥的访问密码 
-keystore 生成的密钥库文件名,扩展名是jks
-storepass 密钥库的访问密码,用来打开jks文件

生成密钥cmd窗口
输入信息后,即可在当前目录生成jks文件。

第二步:导出公钥

对于微服务来说,私钥放在认证服务上,其他服务只需要存公钥即可。因为其他服务只做校验,不加密JWT
可以通过openssl导出密钥,即字符串格式的公钥或私钥
openssl需要安装,点击此处跳转下载
安装好之后,增加环境变量:
openssl的环境变量
执行以下命令即可:

keytool -list -rfc --keystore jks文件(包含扩展名.jks) | openssl x509 -inform pem -pubkey

获取密钥
将 -----BEGIN PUBLIC KEY----- 与 -----END PUBLIC KEY----- 中的内容复制,新建文件并存放。
一般是创建public.key
public.key文件内容
将其放入项目的resource目录下,即可
项目目录

第三步:加密JWT

	/**
     * 根据私钥生成JWT令牌
     *
     * @author Eugenema
     * @date 2022/2/19 17:49
     *
     * @param userInfo 用户信息
     *
     * @return 加密后的JWT
     **/
    public String createJWT(UserInfo userInfo){
        //载荷
        Map<String,Object> payload = new HashMap<>(2);
        payload.put("id", userInfo.getId());
        payload.put("userName",userInfo.getName());

		/** 私钥:resource目录下 */
        ClassPathResource keyFileResource = new ClassPathResource("emPerson.jks");
        //创建秘钥工厂,参数为:秘钥文件、秘钥库密码
        //import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
        //pom文件:
        /**
        <dependency>
      		<groupId>org.springframework.security</groupId>
      		<artifactId>spring-security-rsa</artifactId>
      		<version>1.0.11.RELEASE</version>
    	</dependency>
    	*/
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyFileResource, "密钥库密码password".toCharArray());
        //获取秘钥,参数为:别名,秘钥密码
        KeyPair keyContent = keyStoreKeyFactory.getKeyPair("alias别名", "秘钥的密码".toCharArray());
        /** 私钥 */
        PrivateKey privateKey = keyContent.getPrivate();

        //通过JwtHelper生成JWT
        //由于无法设置过期时间,被弃用
//        Jwt jwtContent = JwtHelper.encode(JSON.toJSONString(payload), new RsaSigner(privateKey));
//        String jwtEncoded = jwtContent.getEncoded();
//        System.out.println(jwtEncoded);

		//通过Jwts生成JWT
		//pom文件:
		/**
		<dependency>
      		<groupId>io.jsonwebtoken</groupId>
      		<artifactId>jjwt</artifactId>
      		<version>0.9.0</version>
    	</dependency>
		*/
        JwtBuilder eugeneMa = Jwts.builder()
        		//设置载荷
        		.addClaims(payload)
        		//设置ID,据说能防止重放攻击
                .setId(String.valueOf(System.currentTimeMillis()))
                //设置主题
                .setSubject("eugeneMa")
                //设置签发时间
                .setIssuedAt(new Date())
                //设置过期时间
                .setExpiration(new Date(System.currentTimeMillis() + 30000))
                //设置加密算法及私钥
                .signWith(SignatureAlgorithm.RS256, privateKey);
        //返回JWT
		return eugeneMa.compact();
    }

第四步:解密JWT

	/**
     * 解析JWT
     *
     * @author Eugenema
     * @date 2022/2/19 18:47
     *
     * @param jwt 要解密的JWT
     *
     * @return 解析后的jwt
     **/
    public String parseJwt(String jwt){
    	//通过JwtHelper解析
    	//Jwt token = JwtHelper.decodeAndVerify(jwt, new RsaVerifier(getPubKey));
    	//return token.getClaims();
    	
		//通过Jwts解析
		Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getPubKey()).parseClaimsJws(jwt);
		return claimJws.getBody();
    }

	/**
     * 获取公钥
     *
     * @author Eugenema
     * @date 2022/2/19 19:01
     *
     * @return 公钥,若获取失败则返回null
     **/
    private static PublicKey getPubKey() {
    	//指向resource目录下的公钥文件
        Resource publicKey = new ClassPathResource("public.key");
        try {
            InputStreamReader publicKeyIs = new InputStreamReader(publicKey.getInputStream());
            BufferedReader publicKeyBr = new BufferedReader(publicKeyIs);
            StringBuilder publicKeySb = new StringBuilder();
            String line;
            //将文件中的多行变为一行
            while ((line = publicKeyBr.readLine()) != null) {
                publicKeySb.append(line);
            }
			
			//将String转换成java的PublicKey对象
            byte[] byteKey = Base64.getDecoder().decode(publicKeySb.toString());
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(byteKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePublic(x509EncodedKeySpec);
        } catch (Exception e) {
            logger.error("获取公钥异常!", e);
            return null;
        }
    }
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值