API 系列 - 深入浅出 JSON Web Token

原理#

Web 应用可分为客户端和服务端,这两者之间经常需要进行身份认证。

由于 HTTP 是无状态协议,不能保存认证后的用户状态,因此,每一次发送请求都需要重复的进行认证。

为了解决该问题,通常会使用 Cookie 来管理 Session,来实现用户状态管理。

服务器需要记录 Session 信息,当存在多台服务器时,这些服务器就需要共享 Session。例如,将 Session 数据进行持久化存储,每台服务器都从持久层中获取 Session。这种方案也有风险,当持久层突然失效时,所有登录信息都将失效。

由此可见,服务端保存认证状态是一件棘手的事,既然如此,何不将数据全都保存在客户端呢?Json Web Token 就是基于该思路提出的一种认证方案。

我们从 JSON Web Token 的命名就可以得出一些信息

  • JSON - 数据是 JSON 格式的
  • Web - 是用于 Web 的
  • Token - Token 是令牌的意思,代表了 JWT 是一种认证的凭证

JWT 的认证过程如下:

  1. 客户端发送登录信息(用户 ID,密码)
  2. 服务端基于密钥生成 JWT,返回给客户端
  3. 客户端在接下来的请求中将 Token 放在头部中一起发送给服务端
  4. 服务端对 JWT 进行验证

生成的 JWT 的格式如下

JWT 可分为三部分,用 . 符号隔开

  • Header 头部
  • Payload 负载
  • Signature 签名
    Header 用于携带一些元数据,比如加密算法与类型
Copy Highlighter-hljs
{ "alg": "HS256", "typ": "JWT" }

Payload 携带令牌的具体内容,常用的内容如下

  • iss (issuer) 签发人
  • exp (expiration time) 过期时间
  • sub (subject) 主题
  • aud (audience) 受众
  • nbf (Not Before) 生效时间
  • iat (Issued At) 签发时间
  • jti (JWT ID) 编号
    可根据需要自行选择,也可以自己定义
Copy Highlighter-hljs
{ "sub": "1", "name": "Mind Geek", "admin": true }

Signature 是加密 Header 和 Payload 后得到的签名,防止数据篡改,加密公式如下

Copy Highlighter-hljs
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), mind-geek-jwt )

解读

  • HMACSHA256 是 Header 指定的算法
  • mind-geek-jwt 则是服务端定义的密钥
  • JWT 作为一个令牌(token),有些场合可能会放到 URL 中。Base64 有三个字符 +、/ 和 =,在 URL 里面有特殊含义,所以要被替换 = 被省略、+ 替换成 -,/ 替换成 _ 。这就是 Base64URL 算法。

示例#

加密#

定义 Header

Copy Highlighter-hljs
$header = [ 'alg' => 'HS256', 'typ' => 'JWT', ];

定义 Payload

Copy Highlighter-hljs
$payload = [ 'sub' => 1, 'name' => 'Mind Geek', 'admin' => true, ];

定义密钥

Copy Highlighter-hljs
$secret = 'mind-geek-jwt';

将 Header 和 Payload 数组转化成 Json,再使用 Base64URL 算法进行编码。

Copy Highlighter-hljs
function base64url(string $string) { return str_replace('=', '', strtr(base64_encode($string), '+/', '-_')); } // Header $base64Header = base64url(json_encode($header)); // Payload $base64Payload = base64url(json_encode($payload));

将编码后的 Header 和 Payload 用 . 拼接起来

Copy Highlighter-hljs
$encryp = $base64Header.".".$base64Payload;

对其进行加密,加密后再进行 Base64URL 编码,得到 Signature

Copy Highlighter-hljs
$signature = hash_hmac('sha256', $encryp, $secret, true); $base64Signature = base64url($signature);

拼接之后就是 JWT 字符串了

Copy Highlighter-hljs
$token = $base64Header.".".$base64Payload.".".$base64Signature;

验证#

获取客户端发送的 JWT

Copy Highlighter-hljs
$token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsIm5hbWUiOiJNaW5kIEdlZWsiLCJhZG1pbiI6dHJ1ZX0.0_dneYOin4yWRYlD-KmfvGEY6AhjA_zDyyvPhgYq2sU';

解析 JWT,得到编码过后的 Header、Payload 以及 Signature

Copy Highlighter-hljs
list($base64Header, $base64Payload, $base64Signature) = explode('.', $token);

利用得到的 Header 和 Payload ,以及服务端保存的密钥来计算出签名

Copy Highlighter-hljs
$encryp = $base64Header.".".$base64Payload; $signature = hash_hmac('sha256', $encryp, $secret, true); $computedBase64Signature = base64url($signature);

将计算出来的签名与客户端发送的签名进行对比

Copy Highlighter-hljs
if($computedBase64Signature === $base64Signature){ echo "认证成功!"; }
posted @   caibaotimes  阅读(207)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示
CONTENTS