摘要:头部需要一个头部,用于描述关于该的最基本的信息,例如其类型以及签名所用的算法等。签发者需要准备一个可以确认自己身份的字符串,这个字符串我们称之为。
什么是 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 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
头部 (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 大概是个啥子东西了 --- 就是一个字符串!!!
那么这个字符串如何保证不被篡改呢 ? 这里就要引入 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 AppModulesJWTJWT * @throws AppModulesJWTJWTException */ 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(签名器) 、JWT、JWTManager 四部分,你可以自行扩展 Encoder、Encrypter,从而实现自己的编码和加密方法,感兴趣的同学可以去 github 看看源码 96qbhy/simple-jwt 。有问题欢迎与我讨论,同时欢迎 Issue 和 PR 。
如有错误欢迎指出,谢谢。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/28719.html
摘要:客户端发起非登录请求时,服务端通过中的找到对应的来知道此次请求是谁发出的。数量随着登录用户的增多而增多,存储会增加很多。还记得在上家公司做全干工程师的时候,基本从页面写到运维,当时做登录这块的时候,被session、cookie、token各种概念差点整蒙圈了,上网查询相关概念,发现很多人都是类似的疑惑,比如:showImg(https://user-gold-cdn.xitu.io/201...
摘要:要对进行黑盒测试测试的最好办法是对他们进行黑盒测试,黑盒测试是一种不关心应用内部结构和工作原理的测试方法,测试时系统任何部分都不应该被。此外,有了黑盒测试并不意味着不需要单元测试,针对的单元测试还是需要编写的。 本文首发于之乎专栏前端周刊,全文共 6953 字,读完需 8 分钟,速度需 2 分钟。翻译自:RingStack 的文章 https://blog.risingstack.co...
摘要:微服务架构着重培养通用可重用的服务。服务注册和发现微服务架构下,有大量的微服务需要处理。网关也是获得微服务状态监控信息的中心。实际情况是,微服务和其它企业架构并存。 引言:上篇文章介绍了微服务和单体架构的区别、微服务的设计、消息、服务间通信、数据去中心化,本篇会继续深入微服务,介绍其它特性。 治理去中心化 通常治理的意思是构建方案,并且迫使人们通过努力达到组织的目标。SOA治理指导开发...
摘要:微服务架构着重培养通用可重用的服务。服务注册和发现微服务架构下,有大量的微服务需要处理。网关也是获得微服务状态监控信息的中心。实际情况是,微服务和其它企业架构并存。 引言:上篇文章介绍了微服务和单体架构的区别、微服务的设计、消息、服务间通信、数据去中心化,本篇会继续深入微服务,介绍其它特性。 治理去中心化 通常治理的意思是构建方案,并且迫使人们通过努力达到组织的目标。SOA治理指导开发...
阅读 3078·2021-09-22 15:20
阅读 2602·2019-08-30 15:54
阅读 1967·2019-08-30 14:06
阅读 3118·2019-08-30 13:05
阅读 2458·2019-08-29 18:36
阅读 570·2019-08-29 15:10
阅读 525·2019-08-29 11:17
阅读 821·2019-08-28 18:11