折腾:
【已解决】Django+jwt-token-auth的后端和Antd Pro的Reactjs的前端实现用户名密码错误人性化提示
还是要先去解决:
Django中,jwt的:jwt-token-auth去获取token失败时,自定义返回的错误信息
比如加上
message:获取token失败,可能原因是用户名不存在或密码错误
网上搜:
django jwt-token-auth
django jwt-token-auth response
原来:
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
Custom error messages in Django Rest Framework serializer – Stack Overflow
看到:
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
里面有:JWT_AUTH
此处自己用的是:
djangorestframework-jwt==1.11.0
官网文档:
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
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
就是我们要找的:
当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
}
结果没有被执行到
然后才注意到:
还没有合并到master
所以现在官网文档中:
只有:
‘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
参考:
改为:
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’,)
然后可以输出log后,发现:
当用户名和密码错误时,是不会不会调用:
jwt_response_payload_handler
只有用户名和密码正确,才会生成token,返回response-》调用到
jwt_response_payload_handler
所以此处,jwt_response_payload_handler
就没有override的必要了。
还是要继续去找:
jwt获取token失败时,调用哪个函数
Django JWT_RESPONSE_PAYLOAD_ERROR_HANDLER
感觉是:
还没有合并到master中?
去手动修改代码试试
/usr/local/lib/python3.6/site-packages/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
官网文档:
Github:
https://github.com/GetBlimp/django-rest-framework-jwt
想要在获取token失败时,返回自定义的错误信息,则需要:
先使得djangorestframework-jwt支持(类似于)JWT_RESPONSE_PAYLOAD_ERROR_HANDLER
具体做法,参考:
给本地(安装好的)包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
}
即可返回对应的自定义的错误信息: