Java中JWT的使用的详细教程

 更新时间:2023年02月09日 11:43:48   作者:ccx-lly  
JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输,本文介绍了Java中JWT的使用,需要的朋友可以参考下

JWT简介

JWT全称为Json Web Token

JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。
JWT的认证流程如下:
1 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探
2 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码后拼接签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
3 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可
4 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
5 后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等
6 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

JWT结构

JWT由3部分组成:标头(header)、有效载荷(payLoad)、签名(signature)。
在传输的时候,会将JWT的3部分分别进行Base64编码后用.连接形成最终传输的字符串。

Header

JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存

{
  "alg": "HS256",
  "typ": "JWT"
}

PayLoad

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下

{
  "id": "123",
  "name": "cheng"
}

Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象

注意JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:
header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据
signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。

为了完成签名,除了用到header信息和payload信息外,还需要算法的密钥,也就是secretKey。加密的算法一般有2类:

  • 对称加密:secretKey指加密密钥,可以生成签名与验签
  • 非对称加密:secretKey指私钥,只用来生成签名,不能用来验签(验签用的是公钥)
    JWT的密钥或者密钥对,一般统一称为JSON Web Key,也就是JWK
    到目前为止,jwt的签名算法有三种:
  • HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
  • RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)
  • ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)

Java中使用JWT

我这里用的是java-jwt

引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

对称签名

生成JWT的Token

/**
     * 生成JWT token
     */
    @Test
    void generateToken(){
        //预设一个token过期时间
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR,1);//过期时间为1小时
        String token = JWT.create()
                .withHeader(new HashMap<>())//Header
                .withClaim("userId", 123)//PayLoad
                .withClaim("userName", "程")
                .withClaim("userId1", "456")
                .withExpiresAt(calendar.getTime())//过期时间
                .sign(Algorithm.HMAC256("12345"));//签名用的密钥secret
        System.out.println(token);
    }

利用base64解密工具来查看生成的JWT Token中header和payLoad的明文

解析JWT字符串

    /**
     * 解析jwt字符串
     */
    @Test
    void resolveToken(){
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWRJbnQiOjEyMywidXNlcklkU3RyaW5nIjoiNDU2IiwidXNlck5hbWUiOiLnqIsiLCJleHAiOjE2NzU5MTQzMDZ9.MNC-YCxt8C0t9SNbj3_XMRknkK3-PKBP5xX6_JLB8y8";
        //创建解析对象,使用的算法和secret要和创建token时保持一致
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("12345")).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        System.out.println(decodedJWT.getPayload());//base64编码的payLoad
        Claim userIdInt = decodedJWT.getClaim("userIdInt");
        Claim userIdString = decodedJWT.getClaim("userIdString");
        Claim userName = decodedJWT.getClaim("userName");
        System.out.println("userIdInt:"+userIdInt.asInt());
        System.out.println("userIdString:"+userIdString.asString());
        System.out.println("userName:"+userName.asString());
        System.out.println("过期时间:"+new LocalDateTime(decodedJWT.getExpiresAt()));
    }

如果我们设置的token时间短,比如设置为1秒

那么解析时为抛出如下异常

非对称签名

private static final String RSA_PRIVATE_KEY = "...";
private static final String RSA_PUBLIC_KEY = "...";

    /**
     * 生成token
     * @param payload token携带的信息
     * @return token字符串
     */
public static String getTokenRsa(Map<String,String> payload){
    // 指定token过期时间为7天
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DATE, 7);

    JWTCreator.Builder builder = JWT.create();
    // 构建payload
    payload.forEach((k,v) -> builder.withClaim(k,v));

    // 利用hutool创建RSA
    RSA rsa = new RSA(RSA_PRIVATE_KEY, null);
    // 获取私钥
    RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
    // 签名时传入私钥
    String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.RSA256(null, privateKey));
    return token;
}

    /**
     * 解析token
     * @param token token字符串
     * @return 解析后的token
     */
public static DecodedJWT decodeRsa(String token){
    // 利用hutool创建RSA
    RSA rsa = new RSA(null, RSA_PUBLIC_KEY);
    // 获取RSA公钥
    RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
    // 验签时传入公钥
    JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA256(publicKey, null)).build();
    DecodedJWT decodedJWT = jwtVerifier.verify(token);
    return decodedJWT;
}

到此这篇关于Java中JWT的使用的文章就介绍到这了,更多相关Java中JWT使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java各种比较对象的方式的对比总结

    Java各种比较对象的方式的对比总结

    比较对象是面向对象编程语言的一个基本特征.在本教程中,我们将介绍Java语言的一些特性,这些特性允许我们比较对象.此外,我们还将研究外部库中的这些特性,需要的朋友可以参考下
    2021-06-06
  • springboot + vue 实现递归生成多级菜单(实例代码)

    springboot + vue 实现递归生成多级菜单(实例代码)

    这篇文章主要介绍了springboot + vue 实现递归生成多级菜单,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-12-12
  • Java的覆写操作实例分析

    Java的覆写操作实例分析

    这篇文章主要介绍了Java的覆写操作,结合实例形式分析了java属性覆写及super调用父类方法覆写相关操作技巧,需要的朋友可以参考下
    2019-10-10
  • SpringAnimation 实现菜单从顶部弹出从底部消失动画效果

    SpringAnimation 实现菜单从顶部弹出从底部消失动画效果

    最近做项目遇到这样一个需求,要求实现一种菜单,菜单从顶部弹入,然后从底部消失,顶部弹入时,有一个上下抖动的过程,底部消失时,先向上滑动,然后再向下滑动消失。下面给大家带来了实现代码,感兴趣的朋友一起看看吧
    2018-05-05
  • Java实现迅雷地址转成普通地址实例代码

    Java实现迅雷地址转成普通地址实例代码

    本篇文章主要介绍了Java实现迅雷地址转成普通地址实例代码,非常具有实用价值,有兴趣的可以了解一下。
    2017-03-03
  • jsp、struts、spring、mybatis实现前端页面功能模块化拆分的方案

    jsp、struts、spring、mybatis实现前端页面功能模块化拆分的方案

    这篇文章主要介绍了 jsp、struts、spring、mybatis实现前端页面功能模块化拆分的方案,非常不错,需要的朋友参考下
    2017-01-01
  • java 微信小程序code获取openid的操作

    java 微信小程序code获取openid的操作

    这篇文章主要介绍了java 微信小程序code获取openid的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • java如何自定义注解

    java如何自定义注解

    这篇文章主要介绍了java如何自定义注解问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-02-02
  • fastJson泛型如何转换的实现

    fastJson泛型如何转换的实现

    这篇文章主要介绍了fastJson泛型如何转换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • RestTemplate如何通过HTTP Basic Auth认证示例说明

    RestTemplate如何通过HTTP Basic Auth认证示例说明

    这篇文章主要为大家介绍了RestTemplate如何通过HTTP Basic Auth认证的示例说明,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03

最新评论