JWT介绍及使用
阅读原文时间:2023年07月08日阅读:2

目录

JWT-JSON WEB TOKEN

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

一个JWT由head,payload,signature三部分组成,使用.进行分隔。

例如:ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0hTMjU2Jwp9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJhZG1pbiI6IHRydWUKfQ==.2eab8b1db04001b687461803d759ae849ad795cc9840de1795ccbe54bf4b43d7

header(头部)一般有两个部分:分别是typ和alg。

  • typ:表示json的类型,即用来干什么的,在JWT中默认为"JWT"
  • alg:表示加密的算法,如HS256。

一个标准的jwt header的json文本如下:

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

通过BASE64进行加密获得一个字符串:ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0hTMjU2Jwp9

payload

payload(负载)用来保存需要传递的信息的一个json文本。不用来存储敏感信息。

一个自定义的payload:

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

通过BASE64进行加密获得一个字符串:ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJhZG1pbiI6IHRydWUKfQ==

signature

signature(签名)用来校验当前的jwt是否经过篡改,用来校验其有效性的。

signature是通过header和payload获得的两个BASE64加密后的字符串拼接后进行摘要后得到的一个字符串。

String secret = "secret"; //盐,也就是保存在服务器的私钥,用于校验
String encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload);
String signature = HMACSHA256(encodedString, secret);

这样我们就得到一个signature字符串:

2eab8b1db04001b687461803d759ae849ad795cc9840de1795ccbe54bf4b43d7

将这三者进行拼接,使用.拼接即可得到JWT。

ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0hTMjU2Jwp9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJhZG1pbiI6IHRydWUKfQ==.2eab8b1db04001b687461803d759ae849ad795cc9840de1795ccbe54bf4b43d7

使用SHA256算法将header.payload进行盐值摘要,得到一个字符串signature。

如果和jwt中的signature相同的话,那么表示当前的JWT是由我们下发的。这时,我们就可以根据BASE64进行解密获取其中的数据来进行业务操作。

如果不同的话,那么表示该JWT不是我们下发的,停止之后的操作即可。

对于前台获取数据,我们可以直接将payload进行BASE64解密,因为我们相信该JWT来源,就算是被篡改的,也会被服务端进行校验。

JJWT是一个供生成和解析jwt的java端工具。官方网址

导入依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>

测试代码

User user = new User();
user.setId(1L);
user.setLogin("user");
user.setPassword("user");
user.setRole("ROLE_USER");

String secretKey = "MDk5ZmU2YzdhZWE5NWRhZTU0MjgzMTVmMTkxYTI5ZGJmODc3NWU2ZDc5OWI1YWMxZTE5NWYxZWVhY2VmZGYwMWQ1NmExNjI4M2M2OWUzOGM0Nzg1ZGU2YzgxNWVjYzNhODg4YzE0ODhlZDA0YjZlYTgzYzk3MGE4NWFkMmJmOGI=";
byte[] keyBytes = Decoders.BASE64.decode(secretKey);// 私钥必须使用BASE64来获取bytes
Key key = Keys.hmacShaKeyFor(keyBytes);// 创建自定义私钥
String jws = Jwts.builder().setSubject("Joe").signWith(key).compact();// 官方示例
System.out.println(jws);
String compact = Jwts.builder()
        .setSubject(user.getLogin())
        .signWith(key,SignatureAlgorithm.HS512) // 默认使用HS256加密算法
        .setExpiration(new Date(new Date().getTime() + 10000))
        .compact();
System.out.println(compact);

//需要同时进行测试,否则会报解析异常。
Claims body = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(compact).getBody();
System.out.println(body);
String subject = body.getSubject();
System.out.println(subject);

得到结果:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJKb2UifQ.Y3dJ9pJiIoq3Py4xe445ud6Mk02v-N0rBFa8v3zlwZycjoVXOObRGSuyyMw4vhbhwuGIEj4fdIbe9P68Y8tXaA
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoxNjA2ODA2OTcxfQ.GAw3fSU3r6qq7tcPrjqlj4tVh0tGK-GqWGLfPUQfeVIAvRF2_1GEQySYgoRvVc24fw-AcjFO4UIcuWcrvZO_Rg
{sub=user, exp=1606806971}
user

其中headerbody可以直接BASE64解密获得数据。