Skip to content

认证与授权最佳实践

认证 (Authentication) 和授权 (Authorization) 是后端系统中非常重要的安全机制。本文将介绍认证与授权的核心概念和最佳实践。

1. 基本概念

认证

认证是验证用户身份的过程,确保用户是他们声称的人。常见的认证方式包括:

  • 用户名/密码:最基本的认证方式
  • 令牌:如 JWT、OAuth 令牌
  • 生物识别:如指纹、面部识别
  • 多因素认证:结合多种认证方式

授权

授权是确定用户是否有权限访问特定资源或执行特定操作的过程。常见的授权模型包括:

  • 基于角色的访问控制 (RBAC):根据用户角色授予权限
  • 基于属性的访问控制 (ABAC):根据用户属性授予权限
  • 基于策略的访问控制 (PBAC):根据预定义策略授予权限

2. JWT 认证

什么是 JWT

JWT (JSON Web Token) 是一种基于 JSON 的开放标准,用于在各方之间安全地传输信息。JWT 由三部分组成:

  • 头部 (Header):包含令牌类型和签名算法
  • 载荷 (Payload):包含声明(如用户 ID、角色等)
  • 签名 (Signature):用于验证令牌的完整性

生成 JWT

javascript
const jwt = require('jsonwebtoken');

function generateToken(user) {
  const payload = {
    id: user.id,
    name: user.name,
    role: user.role
  };
  
  const token = jwt.sign(payload, process.env.JWT_SECRET, {
    expiresIn: '1h' // 令牌过期时间
  });
  
  return token;
}

验证 JWT

javascript
const jwt = require('jsonwebtoken');

function verifyToken(token) {
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    return decoded;
  } catch (error) {
    throw new Error('Invalid token');
  }
}

在 Express 中使用 JWT

javascript
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());

// 认证中间件
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Access denied' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(403).json({ error: 'Invalid token' });
  }
}

// 登录路由
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  
  // 验证用户(实际应用中应从数据库查询)
  if (username === 'admin' && password === 'password') {
    const user = {
      id: 1,
      name: 'Admin User',
      role: 'admin'
    };
    
    const token = generateToken(user);
    res.json({ token });
  } else {
    res.status(401).json({ error: 'Invalid credentials' });
  }
});

// 受保护的路由
app.get('/protected', authenticateToken, (req, res) => {
  res.json({ message: `Welcome, ${req.user.name}!` });
});

app.listen(3000, () => {
  console.log('Server running at http://localhost:3000');
});

3. OAuth 2.0

OAuth 2.0 流程

  1. 客户端请求授权:客户端向授权服务器请求授权
  2. 用户授权:用户同意授权
  3. 获取访问令牌:客户端使用授权码获取访问令牌
  4. 访问资源:客户端使用访问令牌访问资源

OAuth 2.0 角色

  • 资源所有者:资源的拥有者
  • 客户端:请求访问资源的应用程序
  • 授权服务器:验证用户身份并颁发令牌
  • 资源服务器:存储和提供资源

OAuth 2.0 授权类型

  • 授权码模式:最常用的授权模式
  • 隐式模式:适用于公共客户端
  • 密码模式:适用于受信任的客户端
  • 客户端凭证模式:适用于服务器间通信

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

角色定义

javascript
const roles = {
  admin: ['create', 'read', 'update', 'delete'],
  user: ['read'],
  guest: []
};

// 授权中间件
function authorize(roles) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Unauthorized' });
    }
    
    const userRole = req.user.role;
    if (!roles.includes(userRole)) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    next();
  };
}

// 使用授权中间件
app.get('/admin', authenticateToken, authorize(['admin']), (req, res) => {
  res.json({ message: 'Admin panel' });
});

5. 安全最佳实践

密码存储

  • 使用 bcrypt 等算法哈希密码
  • 避免存储明文密码
  • 定期更新密码哈希算法

令牌管理

  • 设置合理的令牌过期时间
  • 实现令牌刷新机制
  • 维护令牌黑名单

防止暴力攻击

  • 实现速率限制
  • 账户锁定机制
  • 验证码

安全头部

  • 设置适当的 CORS 策略
  • 使用 HTTPS
  • 设置安全相关的 HTTP 头部

6. 实现示例

完整的认证与授权系统

javascript
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const app = express();
app.use(express.json());

// 模拟用户数据
const users = [
  {
    id: 1,
    username: 'admin',
    password: '$2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW', // password: admin123
    role: 'admin'
  },
  {
    id: 2,
    username: 'user',
    password: '$2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW', // password: admin123
    role: 'user'
  }
];

// 生成令牌
function generateToken(user) {
  return jwt.sign(
    { id: user.id, username: user.username, role: user.role },
    process.env.JWT_SECRET || 'secret_key',
    { expiresIn: '1h' }
  );
}

// 认证中间件
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Access denied' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret_key');
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(403).json({ error: 'Invalid token' });
  }
}

// 授权中间件
function authorize(roles) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Unauthorized' });
    }
    
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    next();
  };
}

// 登录路由
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  
  const user = users.find(u => u.username === username);
  
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  bcrypt.compare(password, user.password, (err, result) => {
    if (err) {
      return res.status(500).json({ error: 'Internal server error' });
    }
    
    if (!result) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    
    const token = generateToken(user);
    res.json({ token });
  });
});

// 受保护的路由
app.get('/protected', authenticateToken, (req, res) => {
  res.json({ message: `Welcome, ${req.user.username}!` });
});

// 管理员路由
app.get('/admin', authenticateToken, authorize(['admin']), (req, res) => {
  res.json({ message: 'Admin panel' });
});

app.listen(3000, () => {
  console.log('Server running at http://localhost:3000');
});

7. 常见问题与解决方案

令牌过期

问题:令牌过期后如何处理? 解决方案:实现令牌刷新机制,当令牌过期时,使用刷新令牌获取新的访问令牌。

密码重置

问题:用户忘记密码如何处理? 解决方案:实现密码重置功能,通过邮件或短信发送重置链接。

会话管理

问题:如何管理用户会话? 解决方案:使用 Redis 等缓存系统存储会话信息,设置合理的会话过期时间。

跨域请求

问题:如何处理跨域请求? 解决方案:使用 CORS 中间件,允许跨域请求。

8. 工具推荐

认证库

  • Passport.js:灵活的认证中间件
  • jsonwebtoken:JWT 实现
  • bcrypt:密码哈希库

安全工具

  • helmet:设置安全相关的 HTTP 头部
  • express-rate-limit:速率限制
  • cors:处理跨域请求

9. 总结

认证与授权是后端系统安全的重要组成部分。通过合理的认证机制和授权策略,我们可以保护系统资源,防止未授权访问。

最佳实践包括:

  • 使用 JWT 进行无状态认证
  • 实现基于角色的访问控制
  • 安全存储密码
  • 合理设置令牌过期时间
  • 实现令牌刷新机制

通过正确实现认证与授权系统,我们可以构建安全、可靠的后端服务。