Java- JWT详解

1.什么是JWT

JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑和自成一体的方式,用于在各方之间作为JSON对象安全地传输信息。这些信息可以被验证和信任,因为它是数字签名的。JWT可以使用秘密(使用HMAC算法)或使用RSAECDSA进行公钥/私钥对进行签名。

以下是JSON Web令牌有用的一些场景:

  • 授权:这是使用JWT的最常见场景。一旦用户登录,每个后续请求都将包括JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用JWT的功能,因为它开销小,并且可以轻松地跨不同域使用。

  • 信息交换:JSON Web令牌是在各方之间安全传输信息的好方法。因为JWT可以签名——例如,使用公钥/私钥对——您可以确定发件人就是他们说的那个人。此外,由于签名是使用标头和有效负载计算的,您还可以验证内容是否未被篡改。

2.JWT结构

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

JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

1.Header

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

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

2.Payload

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

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

这些预定义的字段并不要求强制使用。除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如:userid、username

请注意,默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息

3.Signature

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

HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

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


3、JAVA中使用JWT

JAVA项目中使用JWT,需要去JWT官网(https://jwt.io)查找JWT为JAVA提供的类库,在library页面选择JAVA,Auth0点击进入git页面,这里可以找到依赖坐标。

1、引入依赖

创建maven项目,在pom 中添加依赖
<dependency>
   <groupId>com.auth0</groupId>
   <artifactId>java-jwt</artifactId>
   <version>3.19.1</version>
</dependency>

2、token生成

/**
    * 生成Token
    * @param userId
    * @param username
    * @return
    */
   public static String createToken(String userId,String username){
       Map<String,Object> headerMap = new HashMap<>();
       Calendar cr = Calendar.getInstance();
       cr.add(Calendar.SECOND,100);
       String token = JWT.create().withHeader(headerMap) // 标头(可以省略)
              .withClaim("userId",userId)           //有效负荷(可以根据约定配置多个)
              .withClaim("username",username)
              .withExpiresAt(cr.getTime())        // 设置有效时间
              .sign(Algorithm.HMAC256(SIGN));      // 加签(配置私钥)
       System.out.println("生成的token为:"+token);
       return token;
  }


3、验签方法、解码获取信息、常见异常

/**
    * token 验签
    * token 有效负荷读取
    * @param token
    */
   public static void verifyToken(String token){
       JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SIGN)).build();/
       try{
           DecodedJWT decodedJWT = jwtVerifier.verify(token);//验签
           String userId = decodedJWT.getClaim("userId").asString();//读取有效负荷
           String username = decodedJWT.getClaim("username").asString();//读取有效负荷
           Date expiresAt = decodedJWT.getExpiresAt();//获取token有效期
           System.out.println(userId);
           System.out.println(username);
           System.out.println(expiresAt);
      }catch(TokenExpiredException tokenExpiredException){
           System.out.println("超过有效期");
      }catch(AlgorithmMismatchException algorithmMismatchException){
           System.out.println("算法异常");
      }catch(SignatureVerificationException signatureVerificationException){
           System.out.println("签名错误");
      }
  }

4、整合工具类

public class JWTUtils {
   public  static  String SIGN = "ASDFse@#w";
   /**
    * 生成Token
    * @param map 用户信息(非敏感信息)
    * @return
    */
   public static String createToken(Map<String,String> map){
       JWTCreator.Builder builder = JWT.create();
       //遍历map 存放到payload
       map.forEach((k,v)->{
           builder.withClaim(k,v);
      });
       Calendar cr = Calendar.getInstance();
       cr.add(Calendar.SECOND,100);
       builder.withExpiresAt(cr.getTime());                // 设置有效时间(根据业务需求这是有效时间)
       return builder.sign(Algorithm.HMAC256(SIGN));     // 加签(配置私钥,防止字符串被篡改)
  }

   /**
    * token 验签
    * token 有效负荷读取
    * @param token
    */
   public static DecodedJWT verifyToken(String token){
       return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);//创建验证对象
  }
}

4、Springboot+JWT

    简单做了一个springboot整合JWT的Demo,放到了gitee 上,有兴趣可以看看,主要实现思路采用MyBatis逆向工程生成基础代码,整合JWT工具类实现JWT验证,自定义拦截器进行token鉴权。

https://gitee.com/bartonyy/jjwt.git


新车开拔、有用的话记得点赞+收藏🧐#做项目##Java##技术栈#
全部评论
good😏
1 回复 分享
发布于 2022-04-20 09:26
不错喔
点赞 回复 分享
发布于 2022-04-19 09:07
多谢博主分享
点赞 回复 分享
发布于 2022-04-20 09:26

相关推荐

头像 会员标识
10-31 12:32
已编辑
南京技师学院 Java
说明:这篇文章纯纯碎碎念,别的地方不好发就发这里了。&nbsp;&nbsp;&nbsp;&nbsp;从上家离职到现在也差不多一个半月了,至今仍是0大厂offer,自己真的好菜呀。&nbsp;&nbsp;&nbsp;&nbsp;其实上家的实习体验还是挺好的,带我的mentor就是之前的学长,leader也不push我,问了组内几个校招生,也都能开到30左右,可惜了遇到的时机不对,公司业务偏小众化,title不够,只希望我就待两个月别拉黑我,我秋招还是很想回老东家的。&nbsp;&nbsp;&nbsp;&nbsp;在这一个半月内,大大小小的面试也累计将近30场,国内收我简历的中大厂像唯品会、哈啰、快手、网易、滴滴、美团、字节也面了个遍,剩下的小厂面的不计其数。也越发的让我意识到面试真的是一场运气的较量,在面这些大厂之前,我反复看牛客上的面经,看到大部分都是八股的拷打+简历上项目实现的难点,但到我这就是各种场景题,有时候真的要被面试官天马行空的问题震惊到。像面字节的时候,直接问我Lion这个中间价的底层实现原理,还试图让我手搓一个线程池(不是ThreadPoolExecutor这种传参数就行,要从底层进行实现),这我面个鬼,面试官还是老学长,真的让我泪目。&nbsp;&nbsp;&nbsp;&nbsp;在经历了唯品会,哈啰的kpi面,网易给太少拒了,快手一面挂,字节一面挂,美团排序挂,滴滴现在应该也在排序(过了也不一定去,需要转go,而且base北京,一趟来回直接三天白干)。&nbsp;现在已经面麻木了,也对面试失去了热情,现在在面试的前半个小时,我还可以安心的刷抖音,到面试前10分钟,在老板直骗上重新勾搭几个新目标,这样如果这场面试不过也还有下次机会。&nbsp;&nbsp;&nbsp;&nbsp;马上11月份了,终于有一家大厂hr加了微信要了学信网证明,目前进行offer审批了,希望不要出问题吧,许愿&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update:团子发offer了,团孝子启动!!!#26届实习##美团##字节##滴滴##实习##滴滴##大厂##面试#
投递快手等公司10个岗位
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-08 16:11
点赞 评论 收藏
分享
9 34 评论
分享
牛客网
牛客企业服务