You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Django非用户名/密码认证实现:API Key登录方案咨询

API Key认证方案:适配现有用户名密码系统的实现指南

Absolutely, you can add API Key authentication alongside your existing username/password setup for API access — this is a super common pattern for systems that need to support both human-facing dashboard logins and programmatic API access. Let’s walk through a practical, secure implementation step by step:

1. 设计API Key的结构与生成规则

  • 生成高熵随机值:避免可预测的Key,推荐用加密安全的随机数生成器,比如运行openssl rand -hex 32生成64位十六进制字符串,或者结合用户ID的加盐哈希(绝不要用明文用户ID)。
  • 可选拆分结构:可以把Key分成公钥前缀(比如sk_,用于快速识别Key类型)和私钥主体,方便后续分类管理和快速排查问题。
  • 绑定用户身份:每个API Key必须关联到系统中已有的用户(或专门创建的服务账户),这样可以直接复用现有系统的权限体系,不用重新设计权限逻辑。

2. 安全存储API Key

  • 绝不明文存储:对生成的API Key用慢哈希算法(比如bcrypt、Argon2,不要用MD5/SHA-1这类快速哈希)处理后存入数据库,和用户密码的存储方式保持一致。
  • 存储附加元数据:记录Key的创建时间、过期时间、允许调用的API范围(比如["read:metrics", "write:alerts"])、状态(启用/禁用),方便管理和审计。
  • 示例数据库表结构(伪SQL):
    CREATE TABLE api_keys (
        id INT PRIMARY KEY AUTO_INCREMENT,
        user_id INT REFERENCES users(id),
        hashed_key VARCHAR(255) NOT NULL,
        prefix VARCHAR(10) NOT NULL, -- 如sk_、pk_
        expires_at DATETIME,
        allowed_scopes TEXT, -- 用JSON格式存储权限范围
        is_active BOOLEAN DEFAULT TRUE,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );
    

3. 实现API Key认证流程

  • 约定传递方式:推荐让客户端在HTTP请求的Authorization头中传递,格式为Bearer <API_KEY>,这符合REST规范;也可以用自定义头X-API-Key,但Bearer头的兼容性更好。
  • 核心认证步骤:
    1. 从请求头中提取API Key,校验格式合法性(比如是否包含约定的前缀)。
    2. 根据前缀在数据库中查找对应的哈希记录(如果是全值查询,注意不要泄露存在性信息)。
    3. 用相同的哈希算法验证传入的明文Key和存储的哈希值是否匹配。
    4. 检查Key的状态(是否启用)、是否过期。
    5. 验证通过后,将关联的用户信息挂载到请求对象,沿用现有系统的权限校验逻辑。
  • 示例后端验证逻辑(Node.js伪代码):
    async function authenticateApiKey(req, res, next) {
        const authHeader = req.headers.authorization;
        if (!authHeader || !authHeader.startsWith('Bearer ')) {
            return res.status(401).json({ error: 'Valid API Key required' });
        }
        const apiKey = authHeader.split(' ')[1];
        
        // 提取前缀查询,减少数据库查询压力
        const prefix = apiKey.slice(0, 3);
        const apiKeyRecord = await ApiKey.findOne({ 
            where: { prefix, is_active: true } 
        });
        
        if (!apiKeyRecord) {
            return res.status(401).json({ error: 'Invalid API Key' });
        }
        
        // 验证哈希值
        const isMatch = await bcrypt.compare(apiKey, apiKeyRecord.hashed_key);
        if (!isMatch) {
            return res.status(401).json({ error: 'Invalid API Key' });
        }
        
        // 检查过期时间
        if (apiKeyRecord.expires_at && new Date() > apiKeyRecord.expires_at) {
            return res.status(401).json({ error: 'API Key expired' });
        }
        
        // 挂载用户信息,供后续权限校验使用
        req.user = await User.findByPk(apiKeyRecord.user_id);
        next();
    }
    

4. 集成到现有系统的关键点

  • 区分认证入口:给所有API接口单独配置API Key认证中间件,仪表盘的页面路由仍然使用原有的用户名/密码登录流程,两者互不干扰。
  • 提供Key管理界面:在仪表盘内添加“API Keys”管理页,允许用户生成、查看(明文Key只显示一次,生成后无法再次查看)、禁用、设置过期时间。
  • 增加审计日志:记录每个API Key的调用详情(调用时间、接口、IP地址),方便排查问题和安全审计。

5. 安全最佳实践

  • 强制HTTPS:所有API请求必须通过HTTPS传输,防止API Key被网络窃听。
  • 设置合理过期时间:推荐使用短期Key(比如30天),或允许用户自定义过期时长,降低Key泄露后的风险。
  • 可选IP限制:允许用户指定API Key只能从特定IP段调用,进一步提升安全性。
  • 禁止硬编码:提醒用户不要将API Key硬编码到代码中,推荐用环境变量或专业密钥管理工具存储。

内容的提问来源于stack exchange,提问作者blurt.washday

火山引擎 最新活动