djongo写入MongoDB的token_blacklist_outstandingtoken时第二条注册触发重复键错
问题场景
token_blacklist_outstandingtoken是通过djongo驱动连接MongoDB的Django Rest Framework项目中,SimpleJWT模块存储用户已签发访问/刷新令牌的集合- 异常表现:系统可以为第一位注册用户正常完成令牌数据写入,从第二位用户开始插入对应令牌数据时触发写入失败
- 完整报错栈:
djongo.sql2mongo.SQLDecodeError: FAILED SQL: INSERT INTO "token_blacklist_outstandingtoken" ("user_id", "jti", "token", "created_at", "expires_at") VALUES (%(0)s, %(1)s, %(2)s, %(3)s, %(4)s) Params: [11, 'fe4bbe67b6a4413e81cfca6b4e2919aa', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwOTE3Nzc4MCwianRpIjoiZmU0YmJlNjdiNmE0NDEzZTgxY2ZjYTZiNGUyOTE5YWEiLCJ1c2VyX2lkIjoxMX0.JtyGSL_nJ4iiKXqijvxCkipxMQAxTksFKEP6g5qPGiI', datetime.datetime(2020, 12, 14, 17, 49, 40, 602304), datetime.datetime(2020, 12, 28, 17, 49, 40)] Pymongo error: {'writeErrors': [{'index': 0, 'code': 11000, 'keyPattern': {'jti_hex': 1}, 'keyValue': {'jti_hex': None}, 'errmsg': 'E11000 duplicate key error collection: patientStatus.token_blacklist_outstandingtoken index: token_blacklist_o_jti_hex_d9bdf6f7_uniq dup key: { jti_hex: null }', 'op': {'id': 10, 'user_id': 11, 'jti': 'fe4bbe67b6a4413e81cfca6b4e2919aa', 'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwOTE3Nzc4MCwianRpIjoiZmU0YmJlNjdiNmE0NDEzZTgxY2ZjYTZiNGUyOTE5YWEiLCJ1c2VyX2lkIjoxMX0.JtyGSL_nJ4iiKXqijvxCkipxMQAxTksFKEP6g5qPGiI', 'created_at': datetime.datetime(2020, 12, 14, 17, 49, 40, 602304), 'expires_at': datetime.datetime(2020, 12, 28, 17, 49, 40), '_id': ObjectId('5fd7a5b444a87611a60654ae')}}], 'writeConcernErrors': [], 'nInserted': 0, 'nUpserted': 0, 'nMatched': 0, 'nModified': 0, 'nRemoved': 0, 'upserted': []}
问题成因
核心报错触发点为MongoDB 11000类重复键冲突,冲突的唯一索引字段为jti_hex,所有冲突记录的该字段值均为null
- 表层直接原因:MongoDB对
jti_hex字段设置了唯一约束,第二次插入的文档中jti_hex字段值为null,和集合中已存在的第一条记录的null值重复,违反唯一索引规则 - 根本原因为djongo驱动和SimpleJWT模块的兼容性断层:SimpleJWT为了提升
jti(JWT令牌的唯一UUID标识)的索引查询效率,会自动将UUID格式的jti转换为十六进制字符串存储到jti_hex字段并建立唯一索引,但djongo在执行ORM模型到MongoDB文档的映射时,没有实现这个自动转换逻辑,导致所有写入的文档里jti_hex字段始终为空值 - 首次写入可成功的逻辑解释:集合初始为空,第一条带
jti_hex: null的文档不存在重复值,因此可以正常插入;从第二条记录开始,因为唯一索引的存在,所有空值都会被判定为重复,直接拒绝写入。
解决步骤
- 清理存量无效数据与失效索引
先连接MongoDB删除token_blacklist_outstandingtoken集合中所有jti_hex为空的无效历史记录,再移除残留的token_blacklist_o_jti_hex_d9bdf6f7_uniq唯一索引,避免旧索引规则阻碍后续正常写入 - 修复djongo与SimpleJWT的兼容逻辑,可根据实际项目情况选择实现路径:
- 调整SimpleJWT的模型配置,关闭自动生成
jti_hex字段的逻辑,直接以原始jti字符串建立唯一索引,适配djongo的原生ORM映射规则 - 自定义djongo的文档写入钩子,在数据写入MongoDB前自动将
jti字段的UUID值转换为十六进制字符串,填充到jti_hex字段,保证唯一索引字段能生成独立非空的取值
- 调整SimpleJWT的模型配置,关闭自动生成
- 验证写入链路
重新执行数据库迁移操作,连续模拟2个以上用户的令牌签发流程,确认每条写入的文档都能生成独立非空的唯一索引值,不再触发重复键类的写入报错。
内容的提问来源于stack exchange,提问作者Mohammed Bayomy
相关产品推荐
相关产品推荐

