其中,Header 和 Payload 都是 JSON 对象,Signature 是 Header 和 Payload 的签名,用于验证 Token 的完整性。
{ "alg": "HS256", "typ": "JWT" }
export interface JWTHeader { alg: string; typ: string; }Payload
{ "userId": "1234567890", "username": "admin", "write": true, "exp": 1516239022 }
export interface JWTPayload { userId: string; # 用户ID username: string; # 用户名 write?: boolean; # 管理界面权限 exp?: number; # 过期时间(暂未用到) }Signature
function sign(payload: JWTPayload, secret: string): string { const header = { alg: "HS256", typ: "JWT" }; const headerString = JSON.stringify(header); const payloadString = JSON.stringify(payload); const signatureString = crypto .createHmac("sha256", secret) .update(`${headerString}.${payloadString}`) .digest("hex"); // 使用 `.` 拼接三个部分,这就是一个完整的 JSON Web Token return `${headerString}.${payloadString}.${signatureString}`; }验证 Token
function verify(token: string, secret: string): JWTPayload { const parts = token.split("."); const header = JSON.parse(Buffer.from(parts[0], "base64").toString()); const payload = JSON.parse(Buffer.from(parts[1], "base64").toString()); const signatureString = parts[2]; // 验证算法 if (header.alg !== "HS256") { throw new Error("无效算法"); } // 验证签名 // 通过对解析后的 header 和 payload 再次进行签名,比对两个签名是否一致 const signature = crypto .createHmac("sha256", secret) .update(`${parts[0]}.${parts[1]}`) .digest("hex"); if (signature !== signatureString) { throw new Error("无效签名"); } // 验证过期时间 if (payload.exp && payload.exp < Date.now()) { throw new Error("身份认证已过期"); } return payload; }认证流程
注意:Token 只应该包含可用与验证用户身份的信息,千万不要包含诸如密码之类的敏感信息,因为 Token 并不是加密的,在浏览器中使用函数 atob(token.split('.')[1]) 即可解析到 payload 对象的完整内容。
客户端每次请求都会自动带上 Cookie,服务端可以通过验证 Cookie 中 Token 的有效性来判断用户的身份。
如果验证通过,则继续返回用户需要的资源。
if (!payload.write) { // 处理没有权限的情况 }常见问题