You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免JWT Token过载?基于Node.js jsonwebtoken的权限优化问询

Great question—dealing with JWT bloat when storing granular permissions is super common, especially when users have access to multiple platforms. Let’s break down some practical solutions that avoid hitting the database while keeping your tokens manageable:

1. Compress Permission Data into a Compact Format

Instead of storing verbose objects with full section names and boolean flags, encode your permissions into a much smaller structure to cut down token size drastically:

  • Use bitmasking for read/write flags: Represent read: true as 1 (binary 01), write: true as 2 (binary 10), and both as 3 (binary 11).
  • Shorten section names to single letters or abbreviations (e.g., setup → s, chat → c).
  • Replace the sections array with a key-value object to eliminate redundant sectionName keys.

Example Code:

Encoding Permissions (when signing the JWT):

// Convert full permissions to a compact format
const compactPermissions = user.account_permissions.map(perm => {
  const accessFlag = (perm.read ? 1 : 0) + (perm.write ? 2 : 0);
  return {
    [perm.sectionName.slice(0, 1)]: accessFlag, // Use first letter as section key
    platform: perm.platform,
    school: perm.school
  };
});

const payload = { 
  user: { 
    id: user.id, 
    permissions: compactPermissions 
  } 
};

jwt.sign(payload, config.get('jwtSecret'), { expiresIn: TIME }, (err, token) => {
  if (err) throw err;
  res.json({ token });
});

Decoding & Checking Permissions (in middleware):

// Helper to map short keys back to full section names
const getFullSectionName = (shortKey) => {
  const keyMap = { s: 'setup', c: 'chat', m: 'maintenence', cl: 'classes', i: 'income', a: 'announcements', msg: 'messages' };
  return keyMap[shortKey];
};

// Decode compact permissions in your auth middleware
const decodePermissions = (compactPerms) => {
  return compactPerms.map(perm => {
    const sectionKey = Object.keys(perm).find(k => k !== 'platform' && k !== 'school');
    return {
      sectionName: getFullSectionName(sectionKey),
      read: (perm[sectionKey] & 1) === 1,
      write: (perm[sectionKey] & 2) === 2,
      platform: perm.platform,
      school: perm.school
    };
  });
};

// Use decoded permissions to validate access
const userPermissions = decodePermissions(req.user.permissions);

2. Use JWT + Redis Cached Permissions Hybrid

If even compressed permissions are too big, store only minimal user identity in the JWT, and cache full permissions in a fast in-memory store like Redis (this avoids database calls entirely):

How it works:

  1. When generating the JWT, only include user.id (no permissions).
  2. Immediately after signing the JWT, store the full permissions in Redis using the user ID as the key, with an expiration time matching the JWT's expiresIn.
  3. In your auth middleware, verify the JWT to get the user ID, then fetch permissions from Redis (sub-millisecond call, way faster than database queries).

Example Code:

Signing the JWT & Caching Permissions:

const payload = { user: { id: user.id } };

jwt.sign(payload, config.get('jwtSecret'), { expiresIn: TIME }, async (err, token) => {
  if (err) throw err;
  
  // Cache permissions in Redis with matching expiration
  await redisClient.setEx(
    `user:permissions:${user.id}`,
    TIME,
    JSON.stringify(user.account_permissions)
  );
  
  res.json({ token });
});

Middleware to Fetch Permissions:

const authMiddleware = async (req, res, next) => {
  const token = req.header('x-auth-token');
  if (!token) return res.status(401).json({ msg: 'No token, authorization denied' });

  try {
    const decoded = jwt.verify(token, config.get('jwtSecret'));
    // Fetch cached permissions
    const cachedPermissions = await redisClient.get(`user:permissions:${decoded.user.id}`);
    if (!cachedPermissions) return res.status(401).json({ msg: 'Permissions expired, please re-authenticate' });
    
    req.user = {
      id: decoded.user.id,
      permissions: JSON.parse(cachedPermissions)
    };
    next();
  } catch (err) {
    res.status(401).json({ msg: 'Token is not valid' });
  }
};

3. Store Role-Based Permissions Instead of Granular Ones

If your permissions follow a role-based pattern (e.g., "admin" has full access, "editor" can write to most sections), store the user's role(s) per platform in the JWT instead of every individual section permission:

Example:

Payload with Roles:

const payload = { 
  user: { 
    id: user.id, 
    platformRoles: {
      Management: 'admin',
      SchoolXYZ: 'editor',
      SchoolABC: 'viewer'
    } 
  } 
};

Middleware Permission Check:

// Define role-based permission rules in a config file
const rolePermissions = {
  admin: { 
    setup: { read: true, write: true }, 
    chat: { read: true, write: true }, 
    // ... all other sections
  },
  editor: { 
    setup: { read: true, write: false }, 
    chat: { read: true, write: true }, 
    // ... section-specific rules
  },
  viewer: { 
    setup: { read: true, write: false }, 
    chat: { read: true, write: false }, 
    // ... viewer rules
  }
};

// In middleware, map role to permissions
const currentPlatform = req.headers['x-platform']; // Get platform from request headers
const userRole = req.user.platformRoles[currentPlatform];
req.user.permissions = rolePermissions[userRole];

This works best if your permissions aren't fully custom per user—combine it with compression for any custom exceptions.

4. Split Tokens (Main Token + Permissions Token)

Create two separate JWTs:

  • A main token: Stores basic user info (id, platform) with a longer expiration.
  • A permissions token: Stores granular permissions with a shorter expiration.

Only send the permissions token when accessing routes that need permission checks. Most requests will only carry the small main token, and the larger permissions token is used sparingly. Just ensure both tokens are signed with the same secret, and your middleware validates both when needed.


内容的提问来源于stack exchange,提问作者rerez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 22:02:50