JWT的使用流程
JWT的实现原理
在使用 JWT 的时候,有没有想过,为什么我们需要 JWT?以及它的工作原理是什么?
我们就来对比,传统的 session 和 JWT 的区别
我们以一个用户,获取用户资料的例子
传统的 session 流程
- 浏览器发起请求登陆
- 服务端验证身份,生成身份验证信息,存储在服务端,并且告诉浏览器写入 Cookie
- 浏览器发起请求获取用户资料,此时 Cookie 内容也跟随这发送到服务器
- 服务器发现 Cookie 中有身份信息,验明正身
- 服务器返回该用户的用户资料
JWT 流程
- 浏览器发起请求登陆
- 服务端验证身份,根据算法,将用户标识符打包生成 token, 并且返回给浏览器
- 浏览器发起请求获取用户资料,把刚刚拿到的 token 一起发送给服务器
- 服务器发现数据中有 token,验明正身
- 服务器返回该用户的用户资料
你发现了吗?好些并没有什么区别,除了 session 需要服务端存储一份,而 JWT 不需要
但实际上区别大了去了
- session 存储在服务端占用服务器资源,而 JWT 存储在客户端
- session 存储在 Cookie 中,存在伪造跨站请求伪造攻击的风险
- session 只存在一台服务器上,那么下次请求就必须请求这台服务器,不利于分布式应用
- 存储在客户端的 JWT 比存储在服务端的 session 更具有扩展性
- …
对比完了 session 和 JWT 的区别,下面我们来看看那它的实现原理
JWT 工作原理

首先 JWT 长这个样
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpcCI6IjEyNy4wLjAuMSIsInV1aWQiOiJmZjEyMTJmNS1kOGQxLTQ0OTYtYmY0MS1kMmRkYTczZGUxOWEiLCJpYXQiOjE1Mjc1MjMwMTd9.1C01cpOf1N3M9YAYvQjfcLbDGHZ4iPVncCGIoG-lpO0jHOIA_ZHtSMDvK1nzArLpGK5syQSwExsZJz2FJsd2W2TUiHQYtzmQTU8OBXX6mfSZRlkts675W5_WhIiOEwz69GFSD0AKXZifCRgIpKLC0n273MRMr0wJnuBi9ScfJ7YjSiqCr7qyQ5iXeOdS3ObT3wdjjk-Wu9wbWM7R25TFb-7PEZY7r_e8jmczPCVcNbOYegedu73T4d30kRn2jKufTGntD5hR6T9AQsgAMwVR1edEFflWb772TmrHI7WZOAivsBCN9sr4YiyYMvE8lcz_mBsgsunugGiHA3DGxB2ORbjIC8NPm8FI25zGOh9JIM2r_jFFTIm9GiuKtC8Ck8N3-eWi9u1NgBxwLdgN5JyCORnIOlciQEsScg-3SdCTM5LH_j6CeqQNwJxT4-oENzqLSTDJbP-SOj9nnx8HnJ5wh3n64rAvtc89CeTk7PhWFjksHDifngN-cnaszl5lqoF1enz5i9FYYELSjM-G7jns2SyY1MQeLRjuEDriPZtFaGmTW-RLH3gJfQXtbdpEo0_nHBqXEohwoN_FLKo4BNrEwshpyA7vkBpCQC0QALKyC1_L1Q5qduR6dDcqRozAo2VqJXmAKN0rvhLuIEHZkicOZY0Ds4So4GCcleqvFcxm1HQ
眼睛看仔细一些,你会发现 JWT 里面有两个.
数据格式是这样的 header.payload.signature
我们逐个逐个部分去分析,这个部分到底是干嘛的,有什么用
Header
JWT 的 header 中承载了两部分信息
{
"alg": "RS256",
"typ": "JWT"
}
- alg: 声明加密的算法
- typ: 声明类型
对这个头部信息进行 base64,即可得到 header 部分
const headerBuff = Buffer.from(
JSON.stringify({
alg: "RS256",
typ: "JWT"
})
);
const header = headerBuff.toString("base64");
console.log(header);
// eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
Payload
payload 是主体部分,意为载体,承载着有效的 JWT 数据包,它包含三个部分
- 标准声明
- 公共声明
- 私有声明
标准声明的字段
interface Stantar {
iss?: string; // JWT的签发者
sub?: string; // JWT所面向的用户
aud?: string; // 接收JWT的一方
exp?: number; // JWT的过期时间
nbf?: number; // 在xxx日期之间,该JWT都是可用的
iat?: number; // 该JWT签发的时间
jti?: number; //JWT的唯一身份标识
}
标准中建议使用这些字段,但不强制。
公共声明的字段
interface Public {
[key: string]: any;
}
公共声明字段可以添加任意信息,但是因为可以被解密出来,所以不要存放敏感信息。
私有声明的字段
interface Private {
[key: string]: any;
}
私有声明是 JWT 提供者添加的字段,一样可以被解密,所以也不能存放敏感信息。
上面的 JWT 的 payload 结构是这样的
{
"ip": "127.0.0.1",
"uuid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
"iat": 1527523017
}
同样是通过 base64 加密生成第二部分的 payload
const payloadBuffer = Buffer.from(
JSON.stringify({
ip: "127.0.0.1",
uuid: "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
iat: 1527523017
})
);
const payload = payloadBuffer.toString("base64");
console.log(payload);
// eyJpcCI6IjEyNy4wLjAuMSIsInV1aWQiOiJmZjEyMTJmNS1kOGQxLTQ0OTYtYmY0MS1kMmRkYTczZGUxOWEiLCJpYXQiOjE1Mjc1MjMwMTd9
Signature
signature 是签证信息,该签证信息是通过header和payload,加上secret,通过算法加密生成。
公式 signature = 加密算法(header + "." + payload, 密钥);
上面的 header 中,我们已经定义了加密算法使用 RS256,也已经实现了生成header和payload,下面我们来生成 signature
const crypto = require("crypto");
const sign = crypto.createSign("SHA256");
const secret = `私钥,太长我就不贴出来了`;
sign.write(header + "." + payload);
sign.end();
const signature = sign
.sign(secret, "base64")
// 在JWT库中,已经把这些字符过滤掉了
.replace(/=/g, "")
.replace(/\+/g, "-")
.replace(/\//g, "_");
console.log(signature);
到此,已经实现了如何生成一个 JWT 的 token.
它是如何做身份验证的?
首先,JWT 的 Token 相当是明文,是可以解密的,任何存在 payload 的东西,都没有秘密可言,所以隐私数据不能签发 token。
而服务端,拿到 token 后解密,即可知道用户信息,例如本例中的uuid
有了 uuid,那么你就知道这个用户是谁,是否有权限进行下一步的操作。
Token 的过期时间怎么确定?
payload 中有个标准字段 exp,明确表示了这个 token 的过期时间.
服务端可以拿这个时间与服务器时间作对比,过期则拒绝访问。
如何防止 Token 被串改?
此时 signature字段就是关键了,能被解密出明文的,只有header和payload
假如黑客/中间人串改了payload,那么服务器可以通过signature去验证是否被篡改过。
在服务端在执行一次 signature = 加密算法(header + "." + payload, 密钥);, 然后对比 signature 是否一致,如果一致则说明没有被篡改。
所以为什么说服务器的密钥不能被泄漏。
如果泄漏,将存在以下风险:
- 客户端可以自行签发 token
- 黑客/中间人可以肆意篡改 token
安全性相关
如果加强 JWT 的安全性?
根据我的使用,总结以下几点:
- 缩短 token 有效时间
- 使用安全系数高的加密算法
- token 不要放在 Cookie 中,有 CSRF 风险
- 使用 HTTPS 加密协议
- 对标准字段 iss、sub、aud、nbf、exp 进行校验
- 使用成熟的开源库,不要手贱造轮子
- 特殊场景下可以把用户的 UA、IP 放进 payload 进行校验(不推荐)
JWT的使用流程的更多相关文章
- ASP.NET Core Web API中带有刷新令牌的JWT身份验证流程
ASP.NET Core Web API中带有刷新令牌的JWT身份验证流程 翻译自:地址 在今年年初,我整理了有关将JWT身份验证与ASP.NET Core Web API和Angular一起使用的详 ...
- Django的JWT机制工作流程
https://blog.csdn.net/bin_1022/article/details/81278513 django-rest-framework-jwt token 怎么解码得到用户名? d ...
- Hacking JWT(JSON Web Token)
0x01 JWT工作流程 JSON Web Token(JWT)是一个非常轻巧的规范. 这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息. JWT常被用于前后端分离,可以和Restful ...
- Java JWT: JSON Web Token
Java JWT: JSON Web Token for Java and Android JJWT aims to be the easiest to use and understand libr ...
- [转]Node.js 应用:Koa2 使用 JWT 进行鉴权
本文转自:https://www.cnblogs.com/linxin/p/9491342.html 前言 在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行 ...
- Node.js 应用:Koa2 使用 JWT 进行鉴权
前言 在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作.那么服务器端要如何进行鉴权呢? Json ...
- 第九节:JWT简介和以JS+WebApi为例基于JWT的安全校验
一. 简介 1. 背景 传统的基于Session的校验存在诸多问题,比如:Session过期.服务器开销过大.不能分布式部署.不适合前后端分离的项目. 传统的基于Token的校验需要存储Key-Val ...
- 漫谈JWT
一.JWT简介[对于了解JWT的童鞋,可以直接跳到最后] 咱们就不弄那些乱七八糟的概念,就简单点说一下JWT是什么.有什么和能干什么 1. JWT概念和作用 JWT全称为json web token, ...
- 理解JWT(JSON Web Token)认证及python实践
原文:https://segmentfault.com/a/1190000010312468?utm_source=tag-newest 几种常用的认证机制 HTTP Basic Auth HTTP ...
随机推荐
- 联盟链FISCO BCOS权限控制一览
FISCO BCOS是完全开源的联盟区块链底层技术平台,由金融区块链合作联盟(深圳)(简称金链盟)成立开源工作组通力打造.开源工作组成员包括博彦科技.华为.深证通.神州数码.四方精创.腾讯.微众银行. ...
- Glassfish 设置时区
对于Glassfish domain 或者instance下,某个日志的时区不对,前提是系统时区争取. 可以尝试通过如下命令查看jvm 时区设置 asadmin list-jvm-options 如果 ...
- sed命令用法
Sed 简介 sed 是一种新型的,非交互式的编辑器.它能执行与编辑器 vi 和 ex 相同的编辑任务.sed 编辑器没有提供交互式使用方式,使用者只能在命令行输入编辑命令.指定文件名,然后在屏幕上查 ...
- PHP 扩展篇 _ 持续更新
记住这个网站:http://pecl.php.net/ PHP-Redis扩展更新时间:2019/05/06 PHP安装Redis 1:下载目前最新版的redis插件 wget http://pecl ...
- 更新常用的js工具函数
在手机调试时打印代码<script src="https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js"></ ...
- Codeforces 1167F(计算贡献)
要点 容易想到排序,然后对于每个数: 人的惯性思维做法是:\(a[i]*(rank1的+rank2的+-)\).然而解法巧妙之处在于直接把所有的加和当成一个系数,然后先假装所有情况系数都是1,接着往上 ...
- 《深入理解java虚拟机》笔记(4)对象已死吗
一.垃圾回收器回收的对象 虚拟机内存区域中程序计数器.虚拟机栈.本地方法栈随线程而生,随线程而灭.这3个区域内存分配和回收都具备确定性.因此不需要过多考虑回收问题. 而Java堆和方法区不一样,这部分 ...
- 在 WPF 中的线程
线程处理使程序能够执行并发处理,以便它可以做多个操作一次.节省开发人员从线程处理困难的方式,设计了 WPF (窗口演示文稿基金会).这篇文章可以帮助理解线程在 WPF 中的正确用法. WPF 内部线程 ...
- PreparementStatement接口
1.SQL注入问题在以前过程中,总是采取拼接SQL语句的方式,来实现数据的增删改查! String Sql=select * from user where username="" ...
- 报错:java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.xxx.entity.PersonEntity
报错:java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.xxx.entity.PersonEntity 代 ...