Token 加密实现

前端 Token 生成、加密与验证的实现方式

问题

介绍如何实现 Token 加密,包括 JWT 的生成、签名和验证。

解答

JWT 结构

JWT(JSON Web Token)由三部分组成,用 . 连接:

Header.Payload.Signature
  • Header:算法和类型
  • Payload:数据载荷
  • Signature:签名验证

Node.js 实现 JWT

const crypto = require('crypto');

// Base64URL 编码
function base64UrlEncode(str) {
  return Buffer.from(str)
    .toString('base64')
    .replace(/=/g, '')
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
}

// Base64URL 解码
function base64UrlDecode(str) {
  str = str.replace(/-/g, '+').replace(/_/g, '/');
  // 补齐 padding
  while (str.length % 4) {
    str += '=';
  }
  return Buffer.from(str, 'base64').toString();
}

// 生成签名
function createSignature(header, payload, secret) {
  const data = `${header}.${payload}`;
  return crypto
    .createHmac('sha256', secret)
    .update(data)
    .digest('base64')
    .replace(/=/g, '')
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
}

// 生成 Token
function generateToken(payload, secret, expiresIn = 3600) {
  const header = {
    alg: 'HS256',
    typ: 'JWT'
  };

  // 添加过期时间
  const now = Math.floor(Date.now() / 1000);
  payload = {
    ...payload,
    iat: now,           // 签发时间
    exp: now + expiresIn // 过期时间
  };

  const encodedHeader = base64UrlEncode(JSON.stringify(header));
  const encodedPayload = base64UrlEncode(JSON.stringify(payload));
  const signature = createSignature(encodedHeader, encodedPayload, secret);

  return `${encodedHeader}.${encodedPayload}.${signature}`;
}

// 验证 Token
function verifyToken(token, secret) {
  const parts = token.split('.');
  if (parts.length !== 3) {
    throw new Error('Invalid token format');
  }

  const [encodedHeader, encodedPayload, signature] = parts;

  // 验证签名
  const expectedSignature = createSignature(encodedHeader, encodedPayload, secret);
  if (signature !== expectedSignature) {
    throw new Error('Invalid signature');
  }

  // 解析 payload
  const payload = JSON.parse(base64UrlDecode(encodedPayload));

  // 检查过期时间
  if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
    throw new Error('Token expired');
  }

  return payload;
}

// 使用示例
const secret = 'my-secret-key';
const token = generateToken({ userId: 123, role: 'admin' }, secret, 7200);
console.log('Token:', token);

const decoded = verifyToken(token, secret);
console.log('Decoded:', decoded);

前端存储与使用

// 存储 Token
function saveToken(token) {
  // 推荐使用 httpOnly cookie(需后端配合)
  // 前端可用 localStorage,但要注意 XSS 风险
  localStorage.setItem('token', token);
}

// 获取 Token
function getToken() {
  return localStorage.getItem('token');
}

// 请求拦截器添加 Token
axios.interceptors.request.use(config => {
  const token = getToken();
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器处理过期
axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      // Token 过期,跳转登录
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

使用 jsonwebtoken 库

const jwt = require('jsonwebtoken');

const secret = 'my-secret-key';

// 生成
const token = jwt.sign(
  { userId: 123 },
  secret,
  { expiresIn: '2h' }
);

// 验证
try {
  const decoded = jwt.verify(token, secret);
  console.log(decoded);
} catch (err) {
  console.log('验证失败:', err.message);
}

关键点

  • JWT 由 Header、Payload、Signature 三部分组成,使用 Base64URL 编码
  • 签名使用 HMAC-SHA256 算法,保证数据不被篡改
  • Payload 不加密,敏感信息不要放入
  • Token 存储推荐 httpOnly Cookie,防止 XSS 攻击
  • 前端需处理 Token 过期的自动刷新或重新登录逻辑