JWT的解析和加密。分布式登录 SSO登录 OAuth2 SpringSecurity

一篇文章告诉你JWT的实现原理

传统的 session 流程

  1. 浏览器发起请求登陆
  2. 服务端验证身份,生成身份验证信息,存储在服务端,并且告诉浏览器写入 Cookie
  3. 浏览器发起请求获取用户资料,此时 Cookie 内容也跟随这发送到服务器
  4. 服务器发现 Cookie 中有身份信息,验明正身
  5. 服务器返回该用户的用户资料

JWT 流程

  1. 浏览器发起请求登陆
  2. 服务端验证身份,根据算法,将用户标识符打包生成 token, 并且返回给浏览器
  3. 浏览器发起请求获取用户资料,把刚刚拿到的 token 一起发送给服务器
  4. 服务器发现数据中有 token,验明正身
  5. 服务器返回该用户的用户资料
  1. session 存储在服务端占用服务器资源,而 JWT 存储在客户端
  2. session 存储在 Cookie 中,存在伪造跨站请求伪造攻击的风险
  3. session 只存在一台服务器上,那么下次请求就必须请求这台服务器,不利于分布式应用
  4. 存储在客户端的 JWT 比存储在服务端的 session 更具有扩展性

JWT 工作原理

jwt

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpcCI6IjEyNy4wLjAuMSIsInV1aWQiOiJmZjEyMTJmNS1kOGQxLTQ0OTYtYmY0MS1kMmRkYTczZGUxOWEiLCJpYXQiOjE1Mjc1MjMwMTd9.1C01cpOf1N3M9YAYvQjfcLbDGHZ4iPVncCGIoG-lpO0jHOIA_ZHtSMDvK1nzArLpGK5syQSwExsZJz2FJsd2W2TUiHQYtzmQTU8OBXX6mfSZRlkts675W5_WhIiOEwz69GFSD0AKXZifCRgIpKLC0n273MRMr0wJnuBi9ScfJ7YjSiqCr7qyQ5iXeOdS3ObT3wdjjk-Wu9wbWM7R25TFb-7PEZY7r_e8jmczPCVcNbOYegedu73T4d30kRn2jKufTGntD5hR6T9AQsgAMwVR1edEFflWb772TmrHI7WZOAivsBCN9sr4YiyYMvE8lcz_mBsgsunugGiHA3DGxB2ORbjIC8NPm8FI25zGOh9JIM2r_jFFTIm9GiuKtC8Ck8N3-eWi9u1NgBxwLdgN5JyCORnIOlciQEsScg-3SdCTM5LH_j6CeqQNwJxT4-oENzqLSTDJbP-SOj9nnx8HnJ5wh3n64rAvtc89CeTk7PhWFjksHDifngN-cnaszl5lqoF1enz5i9FYYELSjM-G7jns2SyY1MQeLRjuEDriPZtFaGmTW-RLH3gJfQXtbdpEo0_nHBqXEohwoN_FLKo4BNrEwshpyA7vkBpCQC0QALKyC1_L1Q5qduR6dDcqRozAo2VqJXmAKN0rvhLuIEHZkicOZY0Ds4So4GCcleqvFcxm1HQ

Header

{
  "alg": "RS256",
  "typ": "JWT"
}
  • alg: 声明加密的算法
  • typ: 声明类型
const headerBuff = Buffer.from(
  JSON.stringify({
    alg: "RS256",
    typ: "JWT"
  })
);

const header = headerBuff.toString("base64");

console.log(header);
// eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9

Payload

  • 标准声明
  • 公共声明
  • 私有声明
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;
}
{
  "ip": "127.0.0.1",
  "uuid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
  "iat": 1527523017
}
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

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);

它是如何做身份验证的?

Token 的过期时间怎么确定?

如何防止 Token 被串改?

  • 客户端可以自行签发 token
  • 黑客/中间人可以肆意篡改 token

安全性相关

  1. 缩短 token 有效时间
  2. 使用安全系数高的加密算法
  3. token 不要放在 Cookie 中,有 CSRF 风险
  4. 使用 HTTPS 加密协议
  5. 对标准字段 iss、sub、aud、nbf、exp 进行校验
  6. 使用成熟的开源库,不要手贱造轮子
  7. 特殊场景下可以把用户的 UA、IP 放进 payload 进行校验(不推荐)

猜你喜欢

转载自blog.csdn.net/qq_29580525/article/details/82707659
今日推荐