Skip to content
On this page

认证与授权

认证(Authentication)和授权(Authorization)是网络安全的两个核心概念。认证验证用户的身份,而授权确定用户有权访问哪些资源。

认证 vs 授权

认证 (Authentication)

认证是验证用户身份的过程。系统需要确认用户确实是他们声称的那个人。

常见的认证方式:

  • 用户名和密码
  • 多因素认证 (MFA)
  • 生物识别
  • 证书认证

授权 (Authorization)

授权是在认证成功后,确定用户有权访问哪些资源或执行哪些操作的过程。

常见的授权机制:

  • 基于角色的访问控制 (RBAC)
  • 基于属性的访问控制 (ABAC)
  • 访问控制列表 (ACL)

认证机制

1. 基于会话的认证

传统的 Web 应用通常使用基于会话的认证:

javascript
// 登录处理
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  
  const user = await User.findOne({ username });
  if (user && await bcrypt.compare(password, user.passwordHash)) {
    // 创建会话
    req.session.userId = user.id;
    req.session.authenticated = true;
    
    res.json({ success: true, message: 'Login successful' });
  } else {
    res.status(401).json({ success: false, message: 'Invalid credentials' });
  }
});

// 会话验证中间件
const requireAuth = (req, res, next) => {
  if (req.session.authenticated) {
    next();
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
};

2. JWT (JSON Web Token) 认证

JWT 是一种无状态的认证机制:

javascript
const jwt = require('jsonwebtoken');

// 生成 JWT
function generateToken(user) {
  const payload = {
    userId: user.id,
    username: user.username,
    exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1小时后过期
  };
  
  return jwt.sign(payload, process.env.JWT_SECRET);
}

// 验证 JWT
function verifyToken(token) {
  try {
    return jwt.verify(token, process.env.JWT_SECRET);
  } catch (error) {
    throw new Error('Invalid token');
  }
}

// JWT 认证中间件
const authenticateJWT = (req, res, next) => {
  const authHeader = req.headers.authorization;
  const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

  if (token == null) return res.sendStatus(401);

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
};

3. OAuth 2.0

OAuth 2.0 是一种授权框架,常用于第三方登录:

javascript
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use(new GoogleStrategy({
  clientID: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL: "/auth/google/callback"
}, async (accessToken, refreshToken, profile, done) => {
  // 查找或创建用户
  let user = await User.findOne({ googleId: profile.id });
  if (!user) {
    user = await User.create({
      googleId: profile.id,
      name: profile.displayName,
      email: profile.emails[0].value
    });
  }
  return done(null, user);
}));

授权机制

基于角色的访问控制 (RBAC)

javascript
// 角色定义
const ROLES = {
  ADMIN: 'admin',
  MODERATOR: 'moderator',
  USER: 'user'
};

// 权限定义
const PERMISSIONS = {
  READ_USERS: 'read:users',
  WRITE_USERS: 'write:users',
  DELETE_USERS: 'delete:users'
};

// 角色权限映射
const ROLE_PERMISSIONS = {
  [ROLES.ADMIN]: [PERMISSIONS.READ_USERS, PERMISSIONS.WRITE_USERS, PERMISSIONS.DELETE_USERS],
  [ROLES.MODERATOR]: [PERMISSIONS.READ_USERS, PERMISSIONS.WRITE_USERS],
  [ROLES.USER]: [PERMISSIONS.READ_USERS]
};

// 授权中间件
function requirePermission(permission) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }

    const userPermissions = ROLE_PERMISSIONS[req.user.role] || [];
    if (!userPermissions.includes(permission)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }

    next();
  };
}

// 使用示例
app.delete('/users/:id', 
  authenticateJWT, 
  requirePermission(PERMISSIONS.DELETE_USERS), 
  deleteUserHandler
);

基于属性的访问控制 (ABAC)

javascript
// ABAC 策略引擎
class PolicyEngine {
  evaluate(policy, user, resource, action) {
    // 简化的策略评估
    if (policy.subject && policy.subject !== user.role) {
      return false;
    }
    
    if (policy.action && policy.action !== action) {
      return false;
    }
    
    if (policy.resource && !this.matchResource(policy.resource, resource)) {
      return false;
    }
    
    // 环境条件检查
    if (policy.conditions) {
      for (const condition of policy.conditions) {
        if (!this.evaluateCondition(condition, user, resource)) {
          return false;
        }
      }
    }
    
    return true;
  }
  
  matchResource(pattern, resource) {
    // 简化的资源匹配
    return pattern === resource.type;
  }
  
  evaluateCondition(condition, user, resource) {
    // 简化的条件评估
    if (condition.type === 'owner' && condition.attribute === 'userId') {
      return user.id === resource.userId;
    }
    return true;
  }
}

// 使用示例
const policy = {
  subject: 'user',
  action: 'read',
  resource: 'document',
  conditions: [{
    type: 'owner',
    attribute: 'userId'
  }]
};

安全最佳实践

1. 密码安全

javascript
const bcrypt = require('bcrypt');

// 密码哈希
async function hashPassword(password) {
  const saltRounds = 12;
  return await bcrypt.hash(password, saltRounds);
}

// 密码验证
async function verifyPassword(password, hash) {
  return await bcrypt.compare(password, hash);
}

// 密码强度验证
function validatePasswordStrength(password) {
  const minLength = 8;
  const hasUpperCase = /[A-Z]/.test(password);
  const hasLowerCase = /[a-z]/.test(password);
  const hasNumbers = /\d/.test(password);
  const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);

  return password.length >= minLength &&
         hasUpperCase &&
         hasLowerCase &&
         hasNumbers &&
         hasSpecialChar;
}

2. 速率限制

javascript
const rateLimit = require('express-rate-limit');

// 登录尝试限制
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 5, // 最多5次尝试
  message: 'Too many login attempts, please try again later.',
  standardHeaders: true,
  legacyHeaders: false,
});

// API 调用限制
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 最多100次请求
  skipSuccessfulRequests: true
});

3. 会话管理

javascript
const session = require('express-session');
const MongoStore = require('connect-mongo');

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  store: MongoStore.create({
    mongoUrl: process.env.MONGODB_URI
  }),
  cookie: {
    secure: process.env.NODE_ENV === 'production', // HTTPS only
    httpOnly: true, // 防止 XSS
    maxAge: 1800000, // 30分钟
    sameSite: 'strict' // 防止 CSRF
  }
}));

// 会话轮换
function rotateSession(req, res, next) {
  const oldSession = { ...req.session };
  req.session.regenerate((err) => {
    if (err) {
      return next(err);
    }
    
    // 保留重要会话数据
    Object.keys(oldSession).forEach(key => {
      if (['userId', 'role'].includes(key)) {
        req.session[key] = oldSession[key];
      }
    });
    
    next();
  });
}

常见安全漏洞及防护

1. Session Fixation

javascript
// 登录后轮换会话 ID
app.post('/login', (req, res) => {
  req.session.regenerate((err) => {
    if (err) {
      return res.status(500).json({ error: 'Session error' });
    }
    
    // 设置认证状态
    req.session.userId = user.id;
    req.session.authenticated = true;
    
    res.json({ success: true });
  });
});

2. 权限提升

javascript
// 永远不要信任客户端传来的角色信息
app.put('/users/:id', authenticateJWT, async (req, res) => {
  const { id } = req.params;
  const { role } = req.body; // 不要直接使用!
  
  // 从数据库获取当前用户信息
  const currentUser = await User.findById(req.user.userId);
  
  // 只有管理员才能修改用户角色
  if (currentUser.role !== 'admin') {
    return res.status(403).json({ error: 'Insufficient permissions' });
  }
  
  // 安全地更新用户信息
  await User.findByIdAndUpdate(id, { 
    $unset: { role: '' } // 不直接使用 req.body.role
  });
});

前端安全考虑

1. 令牌存储

javascript
// 不要在 localStorage 中存储敏感令牌(易受 XSS 攻击)
// 使用 httpOnly Cookie 或内存存储

// 内存存储示例
class AuthManager {
  constructor() {
    this.token = null;
  }
  
  setToken(token) {
    this.token = token;
  }
  
  getToken() {
    return this.token;
  }
  
  clearToken() {
    this.token = null;
  }
}

// 自动刷新令牌
async function refreshTokenIfNeeded() {
  const token = authManager.getToken();
  if (token && isTokenExpired(token)) {
    try {
      const newToken = await fetchRefreshToken();
      authManager.setToken(newToken);
    } catch (error) {
      logout();
    }
  }
}

2. 请求安全

javascript
// 添加认证头部
const apiClient = axios.create({
  baseURL: '/api',
  headers: {
    'Content-Type': 'application/json'
  }
});

apiClient.interceptors.request.use(
  (config) => {
    const token = authManager.getToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 处理认证失败
apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      logout();
    }
    return Promise.reject(error);
  }
);

总结

认证和授权是 Web 应用安全的基石。实现时需要注意:

  1. 使用成熟的安全库和框架
  2. 实施多层安全防护
  3. 定期审查和更新安全策略
  4. 监控和记录安全事件
  5. 保持系统和依赖项的更新