热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于jwt你应该知道的事情

什么是jwt?JWT全称叫JSONWebToken,是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。jwt使用场景jwt用图广泛,
什么是 jwt ?

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

jwt 使用场景

jwt 用图广泛,例如授权鉴权等。具体一点的话,假如我们有一个 A 用户想要邀请某用户进入自己的群组,此时 A 用户需要生成一条邀请链接,链接内容大致如下: https://host/group/{group_id}/invite/{invite_user}

此时这个链接点击进去虽然可以实现让用户加入群组,但是用户可以随意更改这个链接的参数,例如改改 group 后面的ID,从而加入其他任意群组,改改 invite 后面的邀请人等等操作。所以这种 URL 并不是安全的,那么这种情况下,我们就可以使用 jwt 来实创建一个安全的邀请链接了。

首先 URL 要简单改一下, https://host/group/invite/{token}
可以看到我们去掉了 groupId 和 inviteUser 参数,添加了一个 token 参数,可想而知, groupId 和 inviteUser 应该是被包含进 token 里面了,如何实现这个看似很神奇的 token 呢? 我们来看看 jwt 的原理吧。

jwt 的组成、原理及实现

在讲 jwt 原理之前得先知道 jwt 由哪些东西组成。

jwt 组成

一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

头部 (Header)

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

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

将上面的 json 字符串使用 base64 进行编码后,可以得到一下内容,我们称其为 JWT 的头部(Header)。

eyJ0eXAiOiJqd3QiLCJhbGciOiJtZDUifQ==

载荷(Payload)

我们先将上面的邀请入群的操作描述成一个 JSON 对象。其中添加了一些其他的信息,帮助今后收到这个 JWT 的服务器理解这个JWT。

{"sub": "1","iss": "http://host/group/invite","iat": 1451888119,"exp": 1454516119,"nbf": 1451888119,"jti": "37c107e4609ddbcc9c096ea5ee76c667","group_id": 1,"invite_user": "A"
}

这里面的前6个字段都是由JWT的标准所定义的。

  • sub: 该 JWT 所面向的用户
  • iss: 该 JWT 的签发者
  • iat(issued at): 在什么时候签发的 token
  • exp(expires): token 什么时候过期
  • nbf(not before):token 在此时间之前不能被接收处理
  • jti:JWT ID为web token 提供唯一标识

将上面的 json 字符串使用 base64 进行编码后,可以得到一下内容,我们称其为 JWT 的载荷(Payload)。

eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvOiIsImV4cCI6MTUyNzY2NzY2MywiaWF0IjoxNTI3NjY0MDYzLCJuYmYiOjE1Mjc2NjQwNjMsImdyb3VwX2lkIjoxLCJpbnZpdGVfdXNlciI6IkEiLCJqdGkiOiJlMjE4ZTJhZDdlYTdmZjUzYTVhM2RlZjA0MmFjMjM4NCJ9

签名(Signature)

在签名之前我们需要先得到用于签名的字符串, 将头部和载荷使用 . 进行拼接(头部在前), 得到用于签名的字符串

eyJ0eXAiOiJqd3QiLCJhbGciOiJtZDUifQ==.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvOiIsImV4cCI6MTUyNzY2NzY2MywiaWF0IjoxNTI3NjY0MDYzLCJuYmYiOjE1Mjc2NjQwNjMsImdyb3VwX2lkIjoxLCJpbnZpdGVfdXNlciI6IkEiLCJqdGkiOiJlMjE4ZTJhZDdlYTdmZjUzYTVhM2RlZjA0MmFjMjM4NCJ9

然后使用签名方法对用于签名的字符串进行签名, 得到如下字符串,即 签名(Signature)

NDljMzljOTkyOGNmYWU1NGEyZDYzMTk5NTNlNGEwZDA=

最后把用于签名的字符串和签名使用 . 进行拼接(签名在后), 即可得到 一个完整的 token。但是,此时的
token 没有带上签发者特有的标志,是可以被伪造的,至于如何解决这个问题我们下面 jwt 具体实现会讲。

jwt 原理

jwt 如何保证安全 ?

上面说完 jwt 组成,相信你已经知道 jwt 大概是个啥子东西了 --- 就是一个字符串!!!
那么这个字符串如何保证不被篡改呢 ? 这里就要引入 secret 了。

回到上面的例子,邀请用户入群这个场景,虽然我们上面把 参数改成了 token 这种形式,但是你可能会发现,这样的 token 别人捕获了之后,任然可以自己伪造一个类似的 token ,因为此时的签名(Signature)并没有签发者特有的身份信息,所有数据都是明文的,所以这样签名是不安全的,应该加上 secret 进行签名。

签发者需要准备一个可以确认自己身份的字符串,这个字符串我们称之为 secret 。以 md5 作为签名方法为例(并不建议使用 md5 作为签名方法),我们只需要将上面准备的 用于签名的字符串简单的与 secret 进行拼接,然后进行 md5 计算,这时候得到的签名是受 secret 值影响的,所以即便他人捕获了之后 token,他仍然不能随意篡改 token 的内容,因为他不知道 secret 和拼接方法,故此时的 token 是安全的,不可被恶意篡改的。

$signatureString = 'pen'; // 原始数据
$secret = 'apple'; // 签发者 secret$originSignature = md5($signatureString .'-'. $secret);
print_r($signature); // apple-pen $signatureString = 'pen'; // 原始数据
$secret = 'pineapple'; // 不一样的 secret $fakeSignature = md5($signatureString .'-'. $secret);
print_r($signature); // pineapple-pen
// 可以看到不一样的 secret 会生成完全不一样的签名,这样我们的数据就可以保证不能被随意篡改了~

jwt 传输的数据会泄露 ?

是的,jwt 的头部和载荷字段都可以被解码(base64 属于编码,是可以被解码的)。所以并不建议用 jwt 传输敏感信息,例如密码,因为这很容易被捕获后解码,从而被窃取。

secret 一个字符串不足以描述签发者信息 ?

我们可以将 签发者信息描述成一个 json ,然后对这个 json 字符串进行编码,这样同样可以得到一个 secret 字符串。

jwt 实现

先来一个最粗暴的 jwt 实现

最简单粗暴的 jwt for php 实现

class JWT
{protected $headers;protected $payload;/*** @return array*/public function getHeaders(): array{return $this->headers;}/*** @return array*/public function getPayload(): array{return $this->payload;}public function __construct(array $headers, array $payload){$this->setHeaders($headers);$this->setPayload($payload);}public function setHeaders(array $headers): void{$this->headers = $headers;}public function setPayload(array $payload): void{$this->payload = $payload;}/*** 获取用于签名的字符串** @return string*/public function signatureStr(): string{$headersStr = $this::encodeStr(json_encode($this->headers));$payloadStr = $this::encodeStr(json_encode($this->payload));return "{$headersStr}.{$payloadStr}";}/*** 编码** @param string $string** @return string*/protected static function encodeStr(string $string): string{return rtrim(strtr(base64_encode($string), '+/', '-_'), '=');}/*** 解码** @param string $string** @return string*/protected static function decodeStr(string $string): string{return base64_decode(strtr($string, '-_', '+/'));}/*** 签名,此时的 secret 为 qbhy** @param string $string** @return string*/protected static function signature(string $string): string{return md5($string . 'qbhy');}/*** 校验签名** @param string $signStr* @param string $sign** @return bool*/protected static function checkSignature(string $signStr, string $sign): bool{return static::signature($signStr) === $sign;}/*** 生成 token** @return string*/public function token(): string{$signStr = $this->signatureStr();$token = $signStr . '.' . $this::signature($signStr);return $token;}/*** 从 token 中获取数据** @param string $token** @return \App\Modules\JWT\JWT* @throws \App\Modules\JWT\JWTException*/public static function fromToken(string $token): JWT{$arr = explode('.', $token);if (count($arr) !== 3) {throw new JWTException('token 错误');}if (!static::checkSignature("{$arr[0]}.{$arr[1]}", $arr[2])) {throw new JWTException('签名错误');}$headers = json_decode(static::decodeStr($arr[0]), true);$payload = json_decode(static::decodeStr($arr[1]), true);return new static($headers, $payload);}}

simple-jwt

这里先安利一下我写的一个基于 php 的 jwt 扩展包 --- 96qbhy/simple-jwt , 这个包实现了完整的 jwt 规范,开箱即用,你可以基于 96qbhy/simple-jwt 来给你的应用添加 jwt 相关功能。

我把 simple-jwt 拆分成,Encoder(编码器)Encrypter(签名器)JWTJWTManager 四部分,你可以自行扩展 EncoderEncrypter,从而实现自己的编码和加密方法,感兴趣的同学可以去 github 看看源码 96qbhy/simple-jwt 。有问题欢迎与我讨论,同时欢迎 IssuePR
如有错误欢迎指出,谢谢。



推荐阅读
  • 深入探索HTTP协议的学习与实践
    在初次访问某个网站时,由于本地没有缓存,服务器会返回一个200状态码的响应,并在响应头中设置Etag和Last-Modified等缓存控制字段。这些字段用于后续请求时验证资源是否已更新,从而提高页面加载速度和减少带宽消耗。本文将深入探讨HTTP缓存机制及其在实际应用中的优化策略,帮助读者更好地理解和运用HTTP协议。 ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
  • 最详尽的4K技术科普
    什么是4K?4K是一个分辨率的范畴,即40962160的像素分辨率,一般用于专业设备居多,目前家庭用的设备,如 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 基于Linux系统的Kickstart自动化服务器部署方案
    本文针对企业需求,提出了一种基于Linux系统的Kickstart自动化服务器部署方案。该方案旨在通过无盘批量安装操作系统,提高企业IT基础设施的部署效率。Kickstart是一种利用Anaconda工具实现服务器自动化安装的技术,能够显著简化和加速操作系统的安装过程。通过详细的实施规划,本文介绍了Kickstart的工作原理及其在实际部署中的应用,为企业提供了高效的自动化部署解决方案。 ... [详细]
  • 用阿里云的免费 SSL 证书让网站从 HTTP 换成 HTTPS
    HTTP协议是不加密传输数据的,也就是用户跟你的网站之间传递数据有可能在途中被截获,破解传递的真实内容,所以使用不加密的HTTP的网站是不 ... [详细]
  • 本文介绍了几种常用的图像相似度对比方法,包括直方图方法、图像模板匹配、PSNR峰值信噪比、SSIM结构相似性和感知哈希算法。每种方法都有其优缺点,适用于不同的应用场景。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • 在PHP中实现腾讯云接口签名,以完成人脸核身功能的对接与签名配置时,需要注意将文档中的POST请求改为GET请求。具体步骤包括:使用你的`secretKey`生成签名字符串`$srcStr`,格式为`GET faceid.tencentcloudapi.com?`,确保参数正确拼接,避免因请求方法错误导致的签名问题。此外,还需关注API的其他参数要求,确保请求的完整性和安全性。 ... [详细]
  • 技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告
    技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告 ... [详细]
  • 本文详细解析了 Python 2.x 版本中 `urllib` 模块的核心功能与应用实例,重点介绍了 `urlopen()` 和 `urlretrieve()` 方法的使用技巧。其中,`urlopen()` 方法用于发送网络请求并获取响应内容,而 `urlretrieve()` 方法则用于下载文件并保存到本地。文章通过具体示例展示了这两个方法在实际开发中的应用场景,帮助读者更好地理解和掌握 `urllib` 模块的使用。 ... [详细]
  • 在高清节目的高比特率传输过程中,使用外接USB硬盘进行时间平移(timeshift)时,出现了性能不足和流数据丢失的问题。通过深入研究,我们发现通过对图像组(GOP)和图像头(I-frame)的精确定位技术进行优化,可以显著提升系统的性能和稳定性。本研究提出了改进的图像组与图像头定位算法,有效减少了数据丢失,提高了流媒体传输的效率和质量。 ... [详细]
  • 本文深入探讨了Java枚举类型的使用与实践,详细解析了枚举的基本用法及其在实际开发中的应用。首先介绍了枚举作为常量的替代方案,自JDK 1.5起,通过枚举可以更加简洁、安全地定义常量,避免了传统方式中可能出现的错误。此外,文章还探讨了枚举在实现单例模式、状态机等场景中的优势,并提供了多个实际案例,帮助开发者更好地理解和运用这一强大的语言特性。 ... [详细]
author-avatar
jing阿囡宝_478
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有