什么是JWT?JWT在Java语言中的使用详解

云纹梦纷蝶 2021-08-12 17:50:23 浏览数 (5488)
反馈

JWT 特点

JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

JWT 不加密的情况下,不能将秘密数据写入 JWT。

JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

1. JWT 的原理

Jwt官网:https://jwt.io/

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

{
  "name": "John Doe",
  "角色": "管理员",
  "到期时间": "2018年7月1日0点0分"
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。

服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

2. JWT 的数据结构

JWT 大概就像下面这样。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4
gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。

JWT 的三个组成部分依次如下。

  • · Header(头部)
  • · Payload(负载)
  • · Signature(签名)
# 写成一行,就是下面的样子
Header.Payload.Signature

2.1 Header

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

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

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。

最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串。

2.2 Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个 JSON 对象也要使用 Base64URL 算法转成字符串。

2.3 Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

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

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

3. 在 Java 中使用

依赖,这里使用的是 jjwt

<!-- Jwt https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt</artifactId>
 <version>0.9.1</version>
</dependency>

使用

class DemoApplicationTests {
 // 加盐秘钥
 private String secret = "jwtSecretValue";

 public static void main(String[] args) {
  // 创建token
  String token = this.createToken(20);
//  String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.L1cCfQpCSzPOrxbHVqiMaT8ndRlZd2PuHzYE2TIqUA0";
  System.out.println("token --> " + token);

  // 解析token
  this.parseToken(token);
 }

 // 创建token
 public String createToken(Integer time) {
  // 过期时间, 默认10秒过期
  time = time == null ? 10 : time;
  long l = new Date().getTime() + time * 1000;
  Date expire = new Date(l);

  // 自定义信息
  Map<String, Object> map = new HashMap<>();
  map.put("name", "admin");

  JwtBuilder claim = Jwts.builder()
    // 设置签名算法和加盐秘钥
    .signWith(SignatureAlgorithm.HS256, secret)
    // 设置过期时间
    .setExpiration(expire)
    // 自定义内容接受一个map
    .setClaims(map)
    // 唯一id {"id": "9527"}
    .setId("9527")
    // JWT的主体 {"sub": "jwtSubject"}
    .setSubject("jwtSubject")
    // jwt的签发时间 {"iat": "1618383146"}
    .setIssuedAt(new Date());
    // 自定义内容{"name": "admin"}
//    .claim("name", "admin");

  String token = claim.compact();

  // 解析token, jwt是经过Base64编码的
//  String[] ts = token.split("\.");
//  for (String s : ts) {
//   System.out.print(s + " --> ");
//   System.out.println(Base64Codec.BASE64.decodeToString(s));
//  }
//  System.out.println("===============================");

  return token;
 }

 // 解析token
 public void parseToken(String token) {
  System.out.println("====================开始解析JWT====================");

  System.out.println("token --> " + token);
  try {
   Claims body = Jwts.parser()
     // 签名秘钥
     .setSigningKey(secret)
     // 要解析的jwt
     .parseClaimsJws(token)
     .getBody();

   System.out.println("id --> " + body.getId());
   System.out.println("sub --> " + body.getSubject());
   System.out.println("自定义内容 name --> " + body.get("name"));
   System.out.println("iat 创建时间 --> " + body.getIssuedAt());
   Date expiration = body.getExpiration();
   System.out.print("过期时间 --> ");
   System.out.println(expiration == null ? expiration : expiration.toLocaleString());
  } catch (Exception e) {
   e.printStackTrace();
   System.out.println("无效Token");
  }
  System.out.println("====================JWT解析结束====================");
 }
}

以上就是关于JWT的基本内容介绍和JWT在Java中的具体使用的文章,想要了解更多Java JWT的其他内容,请多多关注W3Cschool相关内容的文章。如果觉得本篇文章还不错,还希望大家能够多多支持!


0 人点赞