You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django单元测试中JWT解码返回空username问题排查

Django JWT解码后username为空的问题排查与解决

问题场景

以下是生成JWT、测试授权及解码的相关代码,解码后得到的payload中username字段为空:

生成JWT的LoginView代码

class LoginView(APIView):
    @swagger_auto_schema(request_body=UserSerializer)
    def post(self, request):
        username = request.data['email']
        password = request.data['password']

        user = User.objects.filter(username=username).first()

        if user is None:
            raise AuthenticationFailed('User not found!')

        if not user.check_password(password):
            raise AuthenticationFailed('Incorrect password!')

        payload = {
            'username': user.email,
            'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
            'iat': datetime.datetime.utcnow()
        }

        token = jwt.encode(payload, "secret", algorithm="HS256")

        response = Response()

        response.set_cookie(key='jwt', value=token, httponly=True)
        response.data = {
            'jwt': token
        }
        return response

单元测试授权方式

self.client.credentials(HTTP_AUTHORIZATION = resp_login.data.get("jwt"))

JWT解码代码

def get_payload(request):
    token = request.META.get('HTTP_AUTHORIZATION')
    if not token:
        raise AuthenticationFailed('Unauthenticated!')
    try:
        payload = jwt.decode(token, 'secret', options={"verify_signature": False}, algorithms=['HS512'])
    except jwt.ExpiredSignatureError:
        raise AuthenticationFailed('Unauthenticated!')
    return payload

实际解码结果

{'username': '', 'exp': 1691987384, 'iat': 1691983784}

问题根源

  1. 算法不匹配:生成JWT使用的是HS256算法,但解码时指定为HS512。即便关闭了签名验证,部分JWT库在算法不匹配时可能出现解析异常,导致字段值丢失或为空。
  2. payload字段赋值逻辑:生成payload时,username字段赋值为user.email,若数据库中该用户的email字段本身为空,会直接导致payload中的username为空。
  3. 签名验证关闭风险:关闭verify_signature会绕过签名校验,不仅存在安全隐患,也可能掩盖算法不匹配等解析问题。

解决方案

1. 统一JWT算法并开启签名验证

将解码算法改为与生成一致的HS256,同时开启签名验证(移除verify_signature=False),避免解析异常:

def get_payload(request):
    token = request.META.get('HTTP_AUTHORIZATION')
    if not token:
        raise AuthenticationFailed('Unauthenticated!')
    try:
        payload = jwt.decode(token, 'secret', algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
        raise AuthenticationFailed('Token expired!')
    except jwt.InvalidTokenError:
        raise AuthenticationFailed('Invalid token!')
    return payload

2. 调整payload字段赋值逻辑

如果业务上username字段应存储用户名而非邮箱,将payload中的赋值改为user.username,与查询用户的条件保持一致:

payload = {
    'username': user.username,
    'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
    'iat': datetime.datetime.utcnow()
}

若确实需要存储邮箱,需检查数据库中用户的email字段是否存在有效值。

3. 规范Authorization请求头格式

遵循JWT标准格式,在请求头中传递带Bearer 前缀的token,同时在解码时处理前缀(适配生产环境场景):

# 单元测试代码修改
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {resp_login.data.get('jwt')}")

# 解码代码修改
def get_payload(request):
    auth_header = request.META.get('HTTP_AUTHORIZATION')
    if not auth_header or not auth_header.startswith('Bearer '):
        raise AuthenticationFailed('Unauthenticated!')
    token = auth_header.split(' ')[1]
    try:
        payload = jwt.decode(token, 'secret', algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
        raise AuthenticationFailed('Token expired!')
    except jwt.InvalidTokenError:
        raise AuthenticationFailed('Invalid token!')
    return payload

内容的提问来源于stack exchange,提问作者Echchama Nayak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 12:59:53