使用Flask_JWT与Mongoengine时遇'dict' object has no attribute 'id'错误求助
解决Flask_JWT + Mongoengine 报错:'dict' object has no attribute 'id'
这个错误我之前也踩过坑,核心原因其实很直白:Flask-JWT的authenticate方法要求返回用户模型的实例,而不是字典。
看你的代码,大概率是在authenticate方法里查询到用户后,返回了user.to_json()的结果(毕竟你写了这个序列化方法),但这个方法返回的是一个字典对象。当Flask-JWT尝试访问user.id来生成token时,字典自然没有id属性,直接就抛出这个错误了。
具体修复步骤:
1. 修正authenticate方法,返回UserModel实例
把返回字典的逻辑删掉,直接返回查询到的用户对象:
def authenticate(username, password): # 根据用户名查询用户 user = UserModel.objects(username=username).first() # 验证密码(这里必须提一句:实际生产环境绝对不能明文存密码!一定要用bcrypt/pbkdf2等哈希算法加密后再存储) if user and user.password == password: return user # 返回UserModel实例,不是to_json()生成的字典
2. 让Flask-JWT能正确识别用户ID
Mongoengine默认用pk作为主键(类型是ObjectId),而Flask-JWT默认会尝试读取user.id来生成token。这里有两种简单的解决方式:
方式一:给UserModel添加id属性的@property
在你的UserModel里加一个属性方法,把pk转成字符串返回,适配Flask-JWT的默认逻辑:
class UserModel(Document): first_name = StringField(required=True, max_length=50) last_name = StringField(required=True, max_length=50) username = StringField(required=True) password = StringField(required=True, min_length=6) @property def id(self): return str(self.pk) # 把ObjectId转成字符串,方便JWT使用 def to_json(self): return { "id": str(self.pk), "first_name": self.first_name, "last_name": self.last_name, "username": self.username, # 这里强烈建议删掉密码字段!敏感信息绝对不能对外暴露 # "password": self.password }
方式二:自定义Flask-JWT的identity处理函数
如果不想修改模型,可以在初始化JWT的时候,指定identity函数让它用pk来查询用户:
from flask import Flask from flask_jwt import JWT from your_module import UserModel app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secure-secret-key' # 务必设置安全的密钥 def authenticate(username, password): user = UserModel.objects(username=username).first() if user and user.password == password: return user def identity(payload): # payload里的identity就是用户ID,这里用pk查询用户 user_id = payload['identity'] return UserModel.objects(pk=user_id).first() jwt = JWT(app, authenticate, identity)
额外的安全提醒
最后必须强调:明文存储密码是严重的安全漏洞!推荐用bcrypt来处理密码:
# 先安装依赖:pip install bcrypt import bcrypt class UserModel(Document): # ...你的字段定义 def set_password(self, password): # 生成盐并哈希密码 self.password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') def check_password(self, password): # 验证密码是否匹配 return bcrypt.checkpw(password.encode('utf-8'), self.password.encode('utf-8')) # 注册用户时调用set_password加密 user = UserModel(first_name="John", last_name="Doe", username="johndoe") user.set_password("your-secure-password") user.save() # 认证时用check_password验证 def authenticate(username, password): user = UserModel.objects(username=username).first() if user and user.check_password(password): return user
内容的提问来源于stack exchange,提问作者Joseph Vargas
相关产品推荐
相关产品推荐

