玩转 SpringBoot 2 之整合 JWT 上篇

前言

该文主要带你了解什么是 JWT,以及JWT 定义和先关概念的介绍,并通过简单Demo 带你了解如何使用 SpringBoot 2 整合 JWT。
介绍前在这里我们来探讨一下如何学习一门新的技术,我个人总结为 RSA

  1. R:read 去读官方文档 。
  2. S:search 谷歌或百度先关技术文章或 github 去搜索先关信息。
  3. A:ask 可以向技术大牛请教或和自己的同事同学进行探讨。

关于 **RSA **仅仅代码个人的学习观点,只是给读者一个不成熟的小建议哈

JWT 介绍

官网介绍如下:

What is JSON Web Token?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

中文翻译:
JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间作为JSON对象安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。JWTS可以使用秘密(使用HMAC算法)或公钥/私钥对使用RSA或ECDSA来签名。
虽然JWTS可以加密,但也提供保密各方之间,我们将重点放在签名令牌。签名的令牌可以验证包含在其中的声明的完整性,而加密的令牌隐藏这些声明以防其他各方。当令牌使用公钥/私钥对签名时,签名也证明只有持有私钥的方才是签名的方。

JWT 先关概念介绍

使用 JWT 前需要先了解三块内容:

  1. 头部信息
  2. 载荷信息
  3. 签名信息

头部信息

头部信息由2部分组成

  1. 令牌的类型,即 JWT
  2. 使用的签名算法 ,例如HMACSHA256或RSA。

头部信息 JSON 代码如下:

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

然后,这个JSON被编码为 Base64Url,形成 JWT 的第一部分。

载荷信息

其中包含声明(claims),声明可以存放实体(通常是用户)和其他数据的声明,声明包括3种类型

  1. 已注册声明
  2. 公开声明
  3. 私有声明

已注册声明
这些是一组预定义声明,不是强制性的,但建议使用,以提供一组有用的,可互操作的声明。其中一些是:** iss(发行人), exp(到期时间),sub(主题), aud**(观众)

公开声明
可以参考 IANA JSON Web令牌注册表https://www.iana.org/assignments/jwt/jwt.xhtml) 查看公共的声明

私有声明
根据根据自己的业务需要自定义的一些数据格式。
示例有效负载可以是:

{

"sub": "1234567890",

"name": "John Doe",

"admin": true

}
图片

签名信息

这个部分需要 base64 加密后的 头部信息(header) 和 base64 加密后的载荷信息(payload),使用连接组成的字符串,然后通过头部信息(header)中声明的加密方式进行加盐 secret 组合在加密,然后就构成了 JWT 的第三部分。
例如,如果要使用HMAC SHA256算法,将按以下方式创建签名:

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

JWT 简单实用演示介绍

首先去 JWT 官网一探究竟,访问 https://jwt.io 进入 JWT 官网。接下来我们开始学习如果使用JWT,通过点击上图中 LEARN MORE ABOUT JWT 显示如下图:然后点击START USING THE TOOL。

图片

![图片]

图片

![图片]

如下图所示 选择对应的语言的 JWT 使用教程。我们这里介绍是是 标注有:maven:com.auth0 java-jwt 的版本。

图片

查看 JWT GitHub 示例教程和源码。 https://github.com/auth0/java-jwt

图片

在README.md 文件中有使用介绍和示例程序。

图片
图片

GitHub 上的示例相对简单些,接下来我带大家写一个相对详细的Demo。

第一步在SpringBoot 应用中引入JWT 依赖到pom.xml中

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

我们通过三个方法来演示如何使用JWT

  1. createToken() 创建不携带自定义信息的 token
  2. createTokenWithClaim() 创建携带自定义信息的 token
  3. verifyToken() 验证我们的token信息并解析token中的内容

生成不携带自定义信息 JWT token

第一步:构建头部信息

Map<String, Object> map = new HashMap<String, Object>();
map.put("alg", "HS256");
map.put("typ", "JWT");

第二步:构建密钥信息

Algorithm algorithm = Algorithm.HMAC256("secret");

第三步:我们通过定义注册和自定义声明 并组合头部信息和密钥信息生成jwt token

String token = JWT.create()
   .withHeader(map)// 设置头部信息 Header 
   .withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器
   .withSubject("this is test token")//设置 载荷 签名的主题
   // .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
   .withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
   .withIssuedAt(nowDate) //设置 载荷 生成签名的时间
   .withExpiresAt(expireDate)//设置 载荷 签名过期的时间
   .sign(algorithm);//签名 Signature

详细代码如下:

   @Test
   public void createToken() {

       String secret = "secret";// token 密钥
       Algorithm algorithm = Algorithm.HMAC256("secret");

       // 头部信息
       Map<String, Object> map = new HashMap<String, Object>();
       map.put("alg", "HS256");
       map.put("typ", "JWT");

       Date nowDate = new Date();
       Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期
       
       String token = JWT.create()
           .withHeader(map)// 设置头部信息 Header 
           .withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器
           .withSubject("this is test token")//设置 载荷 签名的主题
           // .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
           .withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
           .withIssuedAt(nowDate) //设置 载荷 生成签名的时间
           .withExpiresAt(expireDate)//设置 载荷 签名过期的时间
           .sign(algorithm);//签名 Signature
       Assert.assertTrue(token.length() > 0);
   }
   ```

## 生成携带自定义信息 JWT token
自定义信息通过 withClaim 方法进行添加,具体操作如下:
```java
JWT.create()
   .withHeader(map)
   .withClaim("loginName", "zhuoqianmingyue")
   .withClaim("userName", "张三")
   .withClaim("deptName", "技术部")
   ```
生成携带自定义信息 JWT token 详细代码如下:

```java
   @Test
   public String createTokenWithChineseClaim() {

       Date nowDate = new Date();
       Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期

       Map<String, Object> map = new HashMap<String, Object>();
       map.put("alg", "HS256");
       map.put("typ", "JWT");

       Algorithm algorithm = Algorithm.HMAC256("secret");
       String token = JWT.create().withHeader(map)
               /* 设置 载荷 Payload */
               .withClaim("loginName", "zhuoqianmingyue").withClaim("userName", "张三").withClaim("deptName", "技术部")
               .withIssuer("SERVICE")// 签名是有谁生成 例如 服务器
               .withSubject("this is test token")// 签名的主题
               // .withNotBefore(new Date())//定义在什么时间之前,该jwt都是不可用的
               .withAudience("APP")// 签名的观众 也可以理解谁接受签名的
               .withIssuedAt(nowDate) // 生成签名的时间
               .withExpiresAt(expireDate)// 签名过期的时间
               /* 签名 Signature */
               .sign(algorithm);
       
       Assert.assertTrue(token.length() > 0);
       return token;

验证 JWT Token

第一步:构建密钥信息

 Algorithm algorithm = Algorithm.HMAC256("secret");

第二步:通过密钥信息和签名的发布者的信息生成JWTVerifier (JWT验证类)

JWTVerifier verifier = JWT.require(algorithm)
               .withIssuer("SERVICE")
               .build();
Algorithm algorithm = Algorithm.HMAC256("secret");

不添加 .withIssuer("SERVICE") 也是可以获取 JWTVerifier 。

第三步:通过JWTVerifier 的verify获取 token中的信息。

DecodedJWT jwt = verifier.verify(token);

如下面代码所示就可以获取到我们之前生成 token 的 签名的主题,观众 和自定义的声明信息。

String subject = jwt.getSubject();
List<String> audience = jwt.getAudience();
Map<String, Claim> claims = jwt.getClaims();
for (Entry<String, Claim> entry : claims.entrySet()) {
   String key = entry.getKey();
   Claim claim = entry.getValue();
   System.out.println("key:"+key+" value:"+claim.asString());
}

验证 JWT Token 详细代码如下:

@Test
   public void verifyToken() throws UnsupportedEncodingException {
       String token = createTokenWithChineseClaim2();
       
       Algorithm algorithm = Algorithm.HMAC256("secret");
       JWTVerifier verifier = JWT.require(algorithm).withIssuer("SERVICE").build(); // Reusable verifier instance
       DecodedJWT jwt = verifier.verify(token);
       
       String subject = jwt.getSubject();
       List<String> audience = jwt.getAudience();
       Map<String, Claim> claims = jwt.getClaims();
       for (Entry<String, Claim> entry : claims.entrySet()) {
           String key = entry.getKey();
           Claim claim = entry.getValue();
           log.info("key:" + key + " value:" + claim.asString());
       }
       Claim claim = claims.get("loginName");

       log.info(claim.asString());
       log.info(subject);
       log.info(audience.get(0));

   }
   public String createTokenWithChineseClaim2() throws UnsupportedEncodingException {

       Date nowDate = new Date();
       Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期

       Map<String, Object> map = new HashMap<String, Object>();
       map.put("alg", "HS256");
       map.put("typ", "JWT");

       User user = new User();
       user.setUserNaem("张三");
       user.setDeptName("技术部");
       Gson gson = new Gson();
       String userJson = gson.toJson(user);

       String userJsonBase64 = BaseEncoding.base64().encode(userJson.getBytes());

       Algorithm algorithm = Algorithm.HMAC256("secret");
       String token = JWT.create().withHeader(map)

               .withClaim("loginName", "zhuoqianmingyue").withClaim("user", userJsonBase64).withIssuer("SERVICE")// 签名是有谁生成
               .withSubject("this is test token")// 签名的主题
               // .withNotBefore(new Date())//该jwt都是不可用的时间
               .withAudience("APP")// 签名的观众 也可以理解谁接受签名的
               .withIssuedAt(nowDate) // 生成签名的时间
               .withExpiresAt(expireDate)// 签名过期的时间
               .sign(algorithm);//签名 Signature

       return token;
   }
   ```

## 小结

JWT 就是一个生成 Token 的工具,如果不使用 JWT 我们也可以根据自己加密规则生成 Token。只不过 JWT 规范了生成 Token 定义了一个标准而已。JWT 的核心的功能就是:生成Token、解析Token。在 玩转 SpringBoot 2 之整合 JWT 下篇中将带大家通过一个接口登录的案例简单介绍 JWT 实战操作。

## 代码示例
具体代码示例请查看我的GitHub 仓库 springbootexamples 中的 spring-boot-2.x-jwt 下 src/test/java JWTDemo.java文件。

GitHub:https://github.com/zhuoqianmingyue/springbootexamples

## 参考文献
https://jwt.io/introduction/
https://github.com/auth0/java-jwt


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容