最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】Django中如何自定义JWT获取token失败时返回的错误信息

Django crifan 4496浏览 0评论

折腾:

【已解决】Django+jwt-token-auth的后端和Antd Pro的Reactjs的前端实现用户名密码错误人性化提示

还是要先去解决:

Django中,jwt的:jwt-token-auth去获取token失败时,自定义返回的错误信息

比如加上

message:获取token失败,可能原因是用户名不存在或密码错误

网上搜:

django jwt-token-auth

django jwt-token-auth response

Django REST framework JWT

原来:

    url(r’^api-token-auth/’, obtain_jwt_token),

是标准写法,用的是库jwt-token-auth内置的函数

并不方便像

JSON web token based authentication in Django – Python Pandemonium – Medium

中实现的

           return HttpResponse(

              json.dumps(jwt_token),

              status=200,

              content_type="application/json"

            )

        else:

            return Response(

              json.dumps({‘Error’: "Invalid credentials"}),

              status=400,

              content_type="application/json"

            )

可以在response返回对应的出错的具体信息,比如用户名不存在

所以此处感觉是:

要么是自定义实现django的jwt的token获取失败时的返回信息 -》能知道具体错误的原因-〉wen前端获取并显示错误

要么是 web前端中,手动判断user的login的response为空时,就输出显示用户名或密码错误-》但是不知道具体错误原因了

django jwt-token-auth customize fail

Tracking User Login Activity in Django Rest Framework: JWT Authentication

说是Django中有user_login_failed的signal?

具体是:

django.contrib.auth的login() -》 user_logged_in

django.contrib.auth的authenticate() -〉user_login_failed

比较麻烦,不适合此处:

此处可以直接在返回失败时,就知道了

Custom Payload Fields · Issue #145 · GetBlimp/django-rest-framework-jwt

好像可以自定义jwt的一些reponse的函数,去达到自己的目的?

FieldError when using custom user model · Issue #102 · GetBlimp/django-rest-framework-jwt

python – Django rest framework not authenticating custom user model – Stack Overflow

自定义 Django 中的认证机制 | Django documentation | Django

Authentication – Django REST framework

django JSONWebTokenAuthentication custom

django JSONWebTokenAuthentication custom login fail

django jwt   login fail

python 3.x – Django Custom Authentication not working – Stack Overflow

Unable to login when using Django rest framework JWT – Stack Overflow

python 2.7 – How to register users in Django REST framework? – Stack Overflow

How can I change the error message for null for username/password? · Issue #240 · GetBlimp/django-rest-framework-jwt

Custom error messages in Django Rest Framework serializer – Stack Overflow

`JSONWebTokenSerializer` always return "Unable to log in with provided credentials." when the user is not activated. · Issue #440 · GetBlimp/django-rest-framework-jwt

Use JWT token auth for https api failed (return "Authentication credentials were not provided") · Issue #354 · GetBlimp/django-rest-framework-jwt

看到:

https://github.com/GetBlimp/django-rest-framework-jwt/issues/145

JWT_AUTH有多个参数配置

所以去找找其他的

django JWT_AUTH

jpadilla/django-jwt-auth: JSON Web Token Authentication support for Django

GetBlimp/django-rest-framework-jwt: JSON Web Token Authentication support for Django REST Framework

Django REST framework JWT

里面有:JWT_AUTH

此处自己用的是:

djangorestframework-jwt==1.11.0

官网文档:

Django REST framework JWT

obtain_jwt_token fail

jwt.verify fails to throw error for expired tokens · Issue #370 · auth0/node-jsonwebtoken

Using JSON Web Tokens (JWT) to Authenticate Requests to REST Resources in Drupal 8

Authenticating via JWT using Django, Axios, and Vue

custom jwt_response_payload_handler

Add a function to return custom response after login failed by mymusise · Pull Request #317 · GetBlimp/django-rest-framework-jwt

python – Where can I override jwt_response_payload_handler method? – Stack Overflow

python – Django Rest Framework JWT – Custom Payload with Extend User – Stack Overflow

django – Error overriding jwt_response_payload_handler to add serialized user – Stack Overflow

Add a function to return custom response after login failed by mymusise · Pull Request #317 · GetBlimp/django-rest-framework-jwt

就是我们要找的:

当jwt获取token失败时:

可以返回自定义的错误信息

不过再去改代码之前,先去看看之前的:

【已解决】js中http的response中body的ReadableStream是什么类型和含义

同时也清楚了,此处jwt去获取token的话,返回的是:

{"non_field_errors":["无法使用提供的认证信息登录。"]}

而不是:原以为的,之前同事说的,他已经返回用户名密码错误的信息了

用:

JWT_AUTH = {

    ‘JWT_RESPONSE_PAYLOAD_ERROR_HANDLER’: ‘apps.util.jwt.jwt_response_payload_error_handler’,

}

以及:

/xx/apps/util/jwt.py

import logging

def jwt_response_payload_error_handler(serializer, request=None):

    logging.info("JWT: serializer=%s", serializer)

    return {

        ‘status’: 1,

        ‘message’: "login failed",

        ‘detail’: serializer.errors

    }

结果没有被执行到

然后才注意到:

Add a function to return custom response after login failed by mymusise · Pull Request #317 · GetBlimp/django-rest-framework-jwt

还没有合并到master

所以现在官网文档中:

Django REST framework JWT

只有:

‘JWT_RESPONSE_PAYLOAD_HANDLER’: ‘rest_framework_jwt.utils.jwt_response_payload_handler’,

所以换成这个试试

结果代码:

import logging

def jwt_response_payload_handler(token, user=None, request=None):

    logging.info("jwt_response_payload_handler: token=%s, user=%s, request=%s", token, user, request)

    return {

        ‘token’: token,

        ‘user’: user

    }

出错:

xx/logs/development.log

ERROR 2018-07-09 15:17:28,164 exception 9314 123145337683968 Internal Server Error: /api/v1/jwt-token-auth/

Traceback (most recent call last):

  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner

    response = get_response(request)

  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 158, in _get_response

    response = self.process_exception_by_middleware(e, request)

  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 156, in _get_response

    response = response.render()

  File "/usr/local/lib/python3.6/site-packages/django/template/response.py", line 106, in render

    self.content = self.rendered_content

  File "/usr/local/lib/python3.6/site-packages/rest_framework/response.py", line 72, in rendered_content

    ret = renderer.render(self.data, accepted_media_type, context)

  File "/usr/local/lib/python3.6/site-packages/rest_framework/renderers.py", line 105, in render

    allow_nan=not self.strict, separators=separators

  File "/usr/local/lib/python3.6/site-packages/rest_framework/utils/json.py", line 28, in dumps

    return json.dumps(*args, **kwargs)

  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 238, in dumps

    **kw).encode(obj)

  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode

    chunks = self.iterencode(o, _one_shot=True)

  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode

    return _iterencode(o, 0)

  File "/usr/local/lib/python3.6/site-packages/rest_framework/utils/encoders.py", line 68, in default

    return super(JSONEncoder, self).default(obj)

  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 180, in default

    o.__class__.__name__)

TypeError: Object of type ‘User’ is not JSON serializable

参考:

Django REST framework JWT

改为:

from apps.user.serializers import UserSerializer

import logging

def jwt_response_payload_handler(token, user=None, request=None):

    logging.info("jwt_response_payload_handler: token=%s, user=%s, request=%s", token, user, request)

    return {

        ‘token’: token,

        ‘user’: UserSerializer(user, context={‘request’: request}).data

    }

然后是可以返回对应的信息了:

但是对于此处的logging,还是无法输出对应的log:

xx/logs/development.log

中只有之前看到的,好像是mysql的log:

DEBUG 2018-07-09 15:27:30,360 utils 9697 123145329958912 (0.001) SELECT @@SQL_AUTO_IS_NULL; args=None

DEBUG 2018-07-09 15:27:30,361 utils 9697 123145329958912 (0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None

DEBUG 2018-07-09 15:27:30,362 utils 9697 123145329958912 (0.001) SELECT VERSION(); args=None

DEBUG 2018-07-09 15:27:30,366 utils 9697 123145329958912 (0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None

DEBUG 2018-07-09 15:27:30,367 utils 9697 123145329958912 (0.001) SELECT `user_user`.`password`, `user_user`.`last_login`, `user_user`.`is_superuser`, `user_user`.`username`, `user_user`.`first_name`, `user_user`.`last_name`, `user_user`.`email`, `user_user`.`is_staff`, `user_user`.`date_joined`, `user_user`.`id`, `user_user`.`is_active`, `user_user`.`name`, `user_user`.`mobile_phone_number` FROM `user_user` WHERE `user_user`.`username` = ‘crifan’; args=(‘crifan’,)

【已解决】Django中如何打印输出log到log文件中

然后可以输出log后,发现:

当用户名和密码错误时,是不会不会调用:

jwt_response_payload_handler

只有用户名和密码正确,才会生成token,返回response-》调用到

jwt_response_payload_handler

所以此处,jwt_response_payload_handler

就没有override的必要了。

还是要继续去找:

jwt获取token失败时,调用哪个函数

Django JWT_RESPONSE_PAYLOAD_ERROR_HANDLER

Add a function to return custom response after login failed by mymusise · Pull Request #317 · GetBlimp/django-rest-framework-jwt

感觉是:

还没有合并到master中?

去手动修改代码试试

/usr/local/lib/python3.6/site-packages/rest_framework_jwt

然后去把:

Add a function to return custom response after login failed by mymusise · Pull Request #317 · GetBlimp/django-rest-framework-jwt

手动修改合并进来

为了防止缓存,再去删除掉:

/usr/local/lib/python3.6/site-packages/rest_framework_jwt/__pycache__

然后再去看看之前自己:

xx/apps/util/jwt.py

中的jwt_response_payload_error_handler:

def jwt_response_payload_error_handler(serializer, request=None):

    logger.info("jwt_response_payload_error_handler")

    logger.info("serializer=%s", serializer)

    logger.info("request=%s", request)

    return {

        ‘status’: 1,

        ‘message’: "login failed",

        ‘detail’: serializer.errors

    }

能否会被调用到

发现可以调用到了:

log中输出了

INFO 2018-07-09 16:16:54,356 jwt 11104 123145436360704 jwt_response_payload_error_handler

INFO 2018-07-09 16:16:54,356 jwt 11104 123145436360704 serializer=JSONWebTokenSerializer(context={‘request’: <rest_framework.request.Request object>, ‘view’: <rest_framework_jwt.views.ObtainJSONWebToken object>}, data={‘username’: ‘crifan’, ‘password’: ’12’}):

username = CharField()

password = PasswordField(write_only=True)

INFO 2018-07-09 16:16:54,358 jwt 11104 123145436360704 request=<rest_framework.request.Request object at 0x10a911908>

web前端log是:

js的fetch后的response的body的ReadableStream

转换为json是:

{"status":1,"message":"login failed","detail":{"non_field_errors":["无法使用提供的认证信息登录。"]}}

然后error代码更新为:

def jwt_response_payload_error_handler(serializer, request=None):

    logger.info("jwt_response_payload_error_handler")

    logger.info("serializer=%s", serializer)

    logger.info("request=%s", request)

    return {

        ‘status’: 400,

        ‘message’: "获取token失败,可能原因是用户名不存在或密码错误",

        ‘detail’: serializer.errors

    }

然后前端console中也可以得到对应的:

【总结】

此处Django中的:

Django的JWT的库:

库的名称和install安装过程,都是叫:

djangorestframework-jwt

安装后,本地包名,叫做:

rest_framework_jwt

官网文档:

Django REST framework JWT

Github:

https://github.com/GetBlimp/django-rest-framework-jwt

想要在获取token失败时,返回自定义的错误信息,则需要:

先使得djangorestframework-jwt支持(类似于)JWT_RESPONSE_PAYLOAD_ERROR_HANDLER

具体做法,参考:

Add a function to return custom response after login failed by mymusise · Pull Request #317 · GetBlimp/django-rest-framework-jwt

给本地(安装好的)包rest_framework_jwt,比如我此处的:

/usr/local/lib/python3.6/site-packages/rest_framework_jwt

合并代码,然后即可支持JWT_RESPONSE_PAYLOAD_ERROR_HANDLER

(加上本身JWT就已支持JWT_RESPONSE_PAYLOAD_HANDLER)

然后Django配置中再去加上

xx/conf/development/settings.py

JWT_AUTH = {

    ‘JWT_RESPONSE_PAYLOAD_HANDLER’:

        ‘apps.util.jwt.jwt_response_payload_handler’,

    ‘JWT_RESPONSE_PAYLOAD_ERROR_HANDLER’:

        ‘apps.util.jwt.jwt_response_payload_error_handler’,

}

和:

xx/apps/util/jwt.py

from apps.user.serializers import UserSerializer

def jwt_response_payload_handler(token, user=None, request=None):

    return {

        ‘token’: token,

        ‘user’: UserSerializer(user, context={‘request’: request}).data

    }

def jwt_response_payload_error_handler(serializer, request=None):

    return {

        ‘status’: 400,

        ‘message’: "获取token失败,可能原因是用户名不存在或密码错误",

        ‘detail’: serializer.errors

    }

即可返回对应的自定义的错误信息:

转载请注明:在路上 » 【已解决】Django中如何自定义JWT获取token失败时返回的错误信息

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.168 seconds, using 22.22MB memory