Django应用中存储用户专属第三方API密钥的最佳实践与可选方案
Django用户第三方API密钥检索的最佳实践
加密存储,杜绝明文
绝对不能将API密钥以明文形式存在数据库中。推荐使用django-fernet-fields提供的EncryptedCharField,它基于Fernet加密算法,自动处理加密和解密,无需手动实现复杂逻辑。也可以用cryptography库自行封装加密方法:from cryptography.fernet import Fernet from django.conf import settings def encrypt_key(raw_key): fernet = Fernet(settings.FERNET_KEY) return fernet.encrypt(raw_key.encode()).decode() def decrypt_key(encrypted_key): fernet = Fernet(settings.FERNET_KEY) return fernet.decrypt(encrypted_key.encode()).decode()注意:
FERNET_KEY要存在环境变量中,不要硬编码到代码里。独立模型,关联用户
建议创建单独的凭证模型,而非直接在User模型加字段,方便后续扩展多第三方服务的场景。示例:from django.db import models from django.contrib.auth.models import User from fernet_fields import EncryptedCharField class ThirdPartyAPICredential(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) api_key = EncryptedCharField(max_length=255) service_name = models.CharField(max_length=100) # 区分不同第三方服务 updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = "第三方API凭证"若用户需绑定多个服务密钥,可改用
ForeignKey并添加unique_together = ('user', 'service_name')约束。基于当前用户检索,严格权限控制
从请求上下文获取当前用户,仅查询该用户关联的密钥,杜绝跨用户访问:def get_current_user_api_key(request, service_name="default"): if not request.user.is_authenticated: return None # 或抛出未认证异常 try: credential = ThirdPartyAPICredential.objects.get( user=request.user, service_name=service_name ) return credential.api_key # Fernet字段自动解密 except ThirdPartyAPICredential.DoesNotExist: return None # 处理无密钥场景,比如引导用户配置在DRF视图中可配合自定义权限类,强化权限校验:
from rest_framework.permissions import BasePermission class IsCredentialOwner(BasePermission): def has_object_permission(self, request, view, obj): return obj.user == request.user缓存优化,减少数据库查询
若密钥检索频繁,可将解密后的密钥缓存到Redis或Django内置缓存中,设置合理过期时间:from django.core.cache import cache def get_current_user_api_key(request, service_name="default"): cache_key = f"user_{request.user.id}_api_key_{service_name}" cached_key = cache.get(cache_key) if cached_key: return cached_key # 数据库查询逻辑... api_key = credential.api_key cache.set(cache_key, api_key, timeout=3600) # 缓存1小时 return api_key密钥更新时要同步清理对应缓存,避免返回旧值。
防止泄露,规避敏感输出
禁止将API密钥写入日志、返回给前端(除非业务必须,且需通过HTTPS传输)。前端若临时使用密钥,用完立即销毁,不要存储在localStorage或cookie中。支持密钥轮换,提供更新入口
开发密钥更新功能,确保用户可随时替换密钥,更新时重新加密存储并清理缓存:def update_api_key(request): if request.method != "POST": return HttpResponseBadRequest("无效请求") new_key = request.POST.get("new_api_key") service_name = request.POST.get("service_name", "default") if not new_key: return HttpResponseBadRequest("请提供新密钥") credential, _ = ThirdPartyAPICredential.objects.get_or_create( user=request.user, service_name=service_name ) credential.api_key = encrypt_key(new_key) credential.save() cache.delete(f"user_{request.user.id}_api_key_{service_name}") return HttpResponse("密钥更新成功")
内容的提问来源于stack exchange,提问作者md12
相关产品推荐
相关产品推荐

