原文地址:JWT 基础教程

博客地址:http://www.extlight.com

一、前言

针对前后端分离的项目,大多是通过 token 进行身份认证来进行交互,今天将介绍一种简单的创建 token 的方式 -- JWT。

二、基本介绍

2.1 定义

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。

2.2 组成部分

一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。前两部分需要经过 Base64 编码,后一部分通过前两部分 Base64 编码后再加密而成。

如果读者不理解上边的陈述,不要紧,下文会详细讲解。

头部(Header)

头部用于描述关于该 JWT 的最基本的信息,例如其类型以及签名所用的算法等,也可以被表示成一个 JSON 对象。例如:

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

在头部指明了签名算法是 HS256 算法。

经过 Base64 编码得到:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9(第一部分)。下文参考资料提供线上加密/解密网址,感兴趣的读者可以自己尝试。

载荷(playload)

载荷就是存放有效信息的地方。这些有效信息包含三个部分:

(1)标准中注册的声明(建议但不强制使用)

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

(2)公共的声明

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。

例如:

{"id":"123456","name":"MoonlightL","sex":"male"}

将该 json 字符串进行 Base64 编码得到:eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9(第二部分)。

(3)私有的声明

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64 是对称解密的,意味着该部分信息可以归类为明文信息。

注意:载荷中的这3个声明并不是都要同时设置。

签证(signature)

jwt的第三部分是一个签证信息。

这个部分需要 Base64 加密后的 header 和 Base64 加密后的 payload 使用 “.” 连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 jwt 的第三部分。

即将 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9 进行 HS256 算法加密(header 定义的)得到:

e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f(第三部分)。

最后,我们将上述的 3 个部分的字符串通过 “.” 进行拼接得到 JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f

为了验证上述生成的 jwt 是否合法,我们可以登录 JWT 官网,官网界面提供 JWT 解密功能,将生成好的 JWT 复制到如下图中进行解密:

在实际开发中,用户登录成功后,后端生成 jwt 返回给前端,之后,前端与后端交互时携带 jwt 让后端验证 jwt 的合法性。

三、实战入门

通过上述的介绍,我们已经了解到什么是 JWT 以及 JWT 生成的规则,现在我们通过代码方式来生成 JWT。

JWT 官网提供了通过不同编程语言来创建 JWT 的工具类/库,此次测试我们选用 JJWT 。

3.1 添加依赖

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>

3.2 编码

import java.security.Key;
import java.util.Date;
import java.util.UUID;
import org.junit.Test;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys; public class JWTTest { @Test
public void testJWT() { Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); System.out.println("=============创建 JWT===========");
Date now = new Date();
JwtBuilder builder= Jwts.builder()
.setId(UUID.randomUUID().toString()) // 载荷-标准中注册的声明
.setSubject("admin") // 载荷-标准中注册的声明
.setIssuedAt(now) // 载荷-标准中注册的声明,表示签发时间
.claim("id", "123456") // 载荷-公共的声明
.claim("name", "MoonlightL") // 载荷-公共的声明
.claim("sex", "male") // 载荷-公共的声明
.signWith(key); // 签证 String jwt = builder.compact();
System.out.println("生成的 jwt :" +jwt); System.out.println("=============解析 JWT==========="); try {
Jws<Claims> result = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt);
// 以下步骤随实际情况而定,只要上一行代码执行不抛异常就证明 jwt 是有效的、合法的
Claims body = result.getBody(); System.out.println("载荷-标准中注册的声明 id:" + body.getId());
System.out.println("载荷-标准中注册的声明 subject:" + body.getSubject());
System.out.println("载荷-标准中注册的声明 issueAt:" + body.getIssuedAt()); System.out.println("载荷-公共的声明的 id:" + result.getBody().get("id"));
System.out.println("载荷-公共的声明的 name:" + result.getBody().get("name"));
System.out.println("载荷-公共的声明的 sex:" + result.getBody().get("sex")); } catch (JwtException ex) { // jwt 不合法或过期都会抛异常
ex.printStackTrace();
}
}
}

执行结果:

=============创建 JWT===========
生成的 jwt :eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI3ZjZmZjRlMC04YjM5LTQyYjUtOGRkNS0xN2M4ZjM5ZmZhNzMiLCJzdWIiOiJhZG1pbiIsImlhdCI6MTU0MzIwNTI4OSwiZXhwIjoxNTQzMjA1MzQ5LCJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.BtEi-GCj5mCunXD_g0Cra7CSE_bMxhTzlOELWKc17I8
=============解析 JWT===========
载荷-标准中注册的声明 id:7f6ff4e0-8b39-42b5-8dd5-17c8f39ffa73
载荷-标准中注册的声明 subject:admin
载荷-标准中注册的声明 issueAt:Mon Nov 26 12:08:09 CST 2018
载荷-公共的声明的 id:123456
载荷-公共的声明的 name:MoonlightL
载荷-公共的声明的 sex:male

注意:加密和解密 JWT 必须是同一个 Key 对象

注意:解密 JWT 时,必须要抓取 JwtException 异常,只要抓取到该异常说明该 JWT 不可用了

JJWT 库还有其他使用方式,具体资料请查看下边提供的参考资料。

四、参考资料

JWT 官网

JJWT 官网

JSON Web Token 入门教程

在线工具

JWT 基础教程的更多相关文章

  1. matlab基础教程——根据Andrew Ng的machine learning整理

    matlab基础教程--根据Andrew Ng的machine learning整理 基本运算 算数运算 逻辑运算 格式化输出 小数位全局修改 向量和矩阵运算 矩阵操作 申明一个矩阵或向量 快速建立一 ...

  2. <<Bootstrap基础教程>> 新书出手,有心栽花花不开,无心插柳柳成荫

    并非闲的蛋疼,做技术也经常喜欢蛋疼,纠结于各种技术,各种需求变更,还有一个很苦恼的就是UI总是那么不尽人意.前不久自己开源了自己做了多年的仓储项目(开源地址:https://github.com/he ...

  3. Memcache教程 Memcache零基础教程

    Memcache是什么 Memcache是danga.com的一个项目,来分担数据库的压力. 它可以应对任意多个连接,使用非阻塞的网络IO.由于它的工作机制是在内存中开辟一块空间,然后建立一个Hash ...

  4. Selenium IDE 基础教程

    Selenium IDE 基础教程 1.下载安装     a 在火狐浏览其中搜索附件组件,查找 Selenium IDE     b 下载安装,然后重启firefox 2.界面讲解      在菜单- ...

  5. html快速入门(基础教程+资源推荐)

    1.html究竟是什么? 从字面上理解,html是超文本标记语言hyper text mark-up language的首字母缩写,指的是一种通用web页面描述语言,是用来描述我们打开浏览器就能看到的 ...

  6. 转发-UI基础教程 – 原生App切图的那些事儿

    UI基础教程 – 原生App切图的那些事儿 转发:http://www.shejidaren.com/app-ui-cut-and-slice.html 移动APP切图是UI设计必须学会的一项技能,切 ...

  7. 【Unity3D基础教程】给初学者看的Unity教程(四):通过制作Flappy Bird了解Native 2D中的RigidBody2D和Collider2D

    作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 引子 在第一篇文章[Unity3D基础教程] ...

  8. oracle基础教程(8)oracle修改字符集

    oracle基础教程(8)oracle修改字符集 1.用dba连接数据库 -->sqlplus / as sysdba 2.查看字符集 -->SELECT parameter, value ...

  9. 改写《python基础教程》中的一个例子

    一.前言 初学python,看<python基础教程>,第20章实现了将文本转化成html的功能.由于本人之前有DIY一个markdown转html的算法,所以对这个例子有兴趣.可仔细一看 ...

随机推荐

  1. OC Foundation框架—字符串

    一.Foundation框架中一些常用的类 字符串型: NSString:不可变字符串 NSMutableString:可变字符串 集合型: 1) NSArray:OC不可变数组 NSMutableA ...

  2. dubbo管理控制台搭建

    1. 从网上下载dubbo管理控制台:dubbo-admin-2.5.4.war 2. 下载tomcat7,解压缩 3. 将tomcat7 webapps/ROOT的文件全部删除,然后把dubbo-a ...

  3. kill qz _e epi,eu,ex,exo out3

    1● epi 在~上,在~周围,在~后面   2● eu 好,优秀     3● ex 出,出去   4● exo 在外面 的,外部的    

  4. poj 2777线段树应用

    敲了n遍....RE愉快的debug了一晚上...发现把#define maxn = 100000 + 10 改成 #define maxn = 100010 就过了....感受一下我呵呵哒的表情.. ...

  5. 【译】MVC3 20个秘方-(15)使用CAPTCHA去防止恶意软件自动提交评论(防灌水)

    [译]MVC3 20个秘方-(15)使用CAPTCHA去防止恶意软件自动提交评论(防灌水)   问题 有种不太幸运的情况,有人用自动程序去提交表单,在整个互联网中造成大量的垃圾.为了防止这种情况的方法 ...

  6. python自动化运维之路06

    python中面向对象编程 编程范式: 编程是 程序 员 用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 , 一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条 ...

  7. java关于null的介绍及比较问题..未完待续

    非对象比较内容,用== 对象比较内容,用相应的方法比如equais. demo1

  8. 需求改进&系统设计

    1.需求&原型改进 针对课堂讨论环节老师和其他组的问题及建议,对修改选题及需求进行修改,发现在原有需求下,无需进行过大修改. 2.系统设计 3.Alpha任务分配计划 待定 4.测试计划 测试 ...

  9. vector中erase用法注意事项

    以前就发现了vector中的erase方法有些诡异(^_^),稍不注意,就会出错.今天又一次遇到了,就索性总结一下,尤其是在循环体中用erase时,由于vector.begin() 和vector.e ...

  10. http指定状态码

    Http状态代码 1.指定状态码: setStatus HttpServletResponse的setStatus方法.如果响应的状态代码比较特殊,并且伴有相关的文档内容,那么一定要在用PrintWr ...