关于Django obtain_jwt_token (authenticate自动验证)获取token返回bad request 400的原因

本文主要探讨Django中obtain_jwt_token获取token返回bad request 400的原因。一是数据库用户名和密码与登录信息不一致,可能是密码明文存储或字段长度不足;二是Django 2.1版本后authenticate会验证is_active,可通过配置取消关联或重写字段解决。

关于Django obtain_jwt_token (authenticate自动验证)获取token返回bad request 400的原因

首先JWT obtain_jwt_token 签发token需要引用django的authenticate()方法,所以问题一般是出在authenticate()
1.普遍都是数据库内的用户名和密码与登陆的用户密码不一致。
1.1一种是创建用户时用create()创建或直接手动录入数据,这种方法会导致出入数据库的密码是明文的,authenticate()验证不了明文密码(因为authenticate()验证的是加密过的密码),存入时使用User.objects.create_user()或者使用check_password()对密码加密。具体怎么做其他博文有写,可以看看。

1.2另一种是比较傻的错误(就是我,搞得我炸裂),由于设置model里面的User表的password字段max_length=50!!!。正常思维一般的密码也不会那么长对吧,想着50长度怎么都够用了(个人项目)。但是由于上面authenticate()需要验证加密的password,所以就不好说了。存入的password经过加密,包括使用python manage.py createsuperuser创建的超级用户的密码也是经过加密的。所以你会发现超级用户也登陆不了。加密过的密码在存入时django会发出一个警告**"Data truncated for column ‘password’ at row 1**,这就说明password只存入了一部分并没有全部存入数据库。所以就导致登陆的密码与数据库取出来解密后的密码不一致的问题,导致登陆失败。只要设置max_length=255,就OK了(男的女的折磨,菜是原罪。。。)

2.就是django2.1 版本后authenticate()还会验证is_active

<think>我们参考用户提供的引用内容,特别是引用[1]和引用[4]中关于生成Token的部分,以及引用[5]中关于使用djangorestframework-simplejwt的内容。用户的需求是:在Django中实现通过查询数据库验证用户登录并返回token的功能。我们可以采用两种方式:1.自定义Token生成和验证(基于数据库存储Token)2.使用第三方库(如djangorestframework-simplejwt)实现JWT下面分别介绍这两种方法:方法一:自定义Token(参考引用[1]和引用[4])步骤:1.创建模型存储Token(如果使用自定义Token)2.编写登录视图,验证用户凭证,生成Token返回3.编写中间件或装饰器进行Token验证(可选,这里先完成登录生成Token部分)方法二:使用djangorestframework-simplejwt(参考引用[5])步骤:1.安装配置djangorestframework-simplejwt2.编写登录视图,使用simplejwt提供的视图或自定义返回格式由于用户要求通过查询数据库验证用户登录并返回token,我们分别实现两种方法的登录部分。注意:方法一中的Token可以是一个随机字符串(如使用secrets.token_hex),也可以使用JWT(如引用[4]中使用了PyJWT)。这里我们分别展示。我们先看方法一(自定义Token)的实现:步骤1:创建模型(如果还没有)我们可以创建一个Token模型,或者将Token直接存储在用户模型上(如引用[4]在User模型上添加token字段)。这里为了简单,我们假设在用户模型上添加token字段。步骤2:编写登录视图参考引用[1]和引用[4]的视图,我们编写如下:但是注意:引用[1]使用了CustomToken模型(一个与用户一对一关联的Token模型),引用[4]使用了JWT生成Token并存储在用户模型的token字段。这里我们提供两种自定义Token生成方式:A.随机字符串(类似引用[1])B.JWT(类似引用[4])我们先看A:随机字符串首先,确保用户模型有一个token字段(或者使用单独的Token模型,这里以用户模型为例)。在views.py中:```pythonfromdjango.contrib.authimportauthenticatefromdjango.httpimportJsonResponseimportsecretsdeflogin(request):ifrequest.method=="POST":username=request.POST.get('username')password=request.POST.get('password')user=authenticate(request,username=username,password=password)ifuserisnotNone:#生成一个随机token(例如64个字符)token=secrets.token_hex(32)#将token保存到用户模型的token字段user.token=tokenuser.save()returnJsonResponse({'token':token,'status':'success'})else:returnJsonResponse({'error':'Invalidcredentials'},status=401)returnJsonResponse({'error':'Methodnotallowed'},status=405)```再看B:使用JWT生成Token(需要安装PyJWT,`pipinstallPyJWT`)```pythonimportjwtimportjsonfromdjango.viewsimportViewfromdjango.httpimportJsonResponsefromdjango.contrib.authimportauthenticatefrom.modelsimportUser#假设用户模型是UserclassLoginView(View):defpost(self,request):#从请求体中获取JSON数据data=json.loads(request.body)username=data.get('username')password=data.get('password')user=authenticate(request,username=username,password=password)ifuserisnotNone:#使用JWT生成Token,需要设置一个密钥(secret)和算法payload={'user_id':user.id,'username':user.username}secret_key='your_secret_key'#这个密钥要保密,且足够复杂token=jwt.encode(payload,secret_key,algorithm='HS256')#注意:jwt.encode返回的可能是字节串,我们解码为字符串ifisinstance(token,bytes):token=token.decode('utf-8')#可以选择将token保存到用户模型(可选)user.token=tokenuser.save()returnJsonResponse({'token':token,'status':'success'})else:returnJsonResponse({'error':'Invalidcredentials'},status=401)```方法二:使用djangorestframework-simplejwt(参考引用[5])步骤1:安装```pipinstalldjangorestframework-simplejwt```步骤2:配置settings.py```pythonINSTALLED_APPS=[...'rest_framework','rest_framework_simplejwt',...]REST_FRAMEWORK={'DEFAULT_AUTHENTICATION_CLASSES':('rest_framework_simplejwt.authentication.JWTAuthentication',)}```步骤3:配置urls.py(为simplejwt提供的TokenObtainPairView添加路由)```pythonfromrest_framework_simplejwt.viewsimportTokenObtainPairView,TokenRefreshViewurlpatterns=[...path('api/token/',TokenObtainPairView.as_view(),name='token_obtain_pair'),path('api/token/refresh/',TokenRefreshView.as_view(),name='token_refresh'),...]```但是,默认的TokenObtainPairView返回的格式是:```json{"access":"xxx","refresh":"yyy"}```如果我们想要像引用[2]那样自定义返回格式(包含用户信息),我们可以自定义一个视图继承TokenObtainPairView,并重写其响应方法。步骤4:自定义返回格式(参考引用[2])在views.py中:```pythonfromrest_framework_simplejwt.viewsimportTokenObtainPairViewfromrest_framework_simplejwt.serializersimportTokenObtainPairSerializerclassMyTokenObtainPairSerializer(TokenObtainPairSerializer):@classmethoddefget_token(cls,user):token=super().get_token(user)#添加自定义声明token['username']=user.usernametoken['email']=user.emailreturntokendefvalidate(self,attrs):data=super().validate(attrs)#添加自定义返回内容data['user']={'id':self.user.id,'username':self.user.username,'email':self.user.email}returndataclassMyTokenObtainPairView(TokenObtainPairView):serializer_class=MyTokenObtainPairSerializer```然后在urls.py中使用这个视图:```pythonpath('api/token/',MyTokenObtainPairView.as_view(),name='token_obtain_pair'),```这样,当用户使用用户名和密码登录时,就会返回包含用户信息的Token。总结:用户可以根据需求选择:1.自定义Token(简单随机字符串或JWT)并存储在用户模型中,然后自己编写验证逻辑(中间件或装饰器)。2.使用djangorestframework-simplejwt,它提供了JWT的生成、刷新和验证功能,并且可以自定义返回格式。注意:方法一需要自己处理Token验证和过期等,方法二则已经实现了这些功能,推荐使用方法二。下面我们针对方法二(使用djangorestframework-simplejwt)给出完整的登录示例步骤。完整步骤(方法二):1.安装djangorestframework-simplejwt2.配置settings.py3.创建自定义的Token序列化器和视图4.配置urls.py这样,当用户向`/api/token/`发送POST请求(包含username和password)时,就会返回Token和用户信息。如果用户希望使用自定义的登录视图(例如不使用simplejwt的视图,而是自己写一个视图,但内部使用simplejwt生成token),也可以:例如,自定义登录视图(使用simplejwt生成token):```pythonfromrest_framework.viewsimportAPIViewfromrest_framework.responseimportResponsefromrest_framework_simplejwt.tokensimportRefreshTokenfromdjango.contrib.authimportauthenticateclassCustomLoginView(APIView):defpost(self,request):username=request.data.get('username')password=request.data.get('password')user=authenticate(username=username,password=password)ifuser:refresh=RefreshToken.for_user(user)returnResponse({'refresh':str(refresh),'access':str(refresh.access_token),'user':{'id':user.id,'username':user.username}})else:returnResponse({'error':'Invalidcredentials'},status=401)```然后把这个视图配置到urls.py。以上提供了多种实现方式,用户可以根据项目需求选择。最后,注意安全:1.使用HTTPS2.密钥要保密(JWT的密钥)3.考虑Token的有效期相关问题:1.如何验证自定义Token的有效性?2.如何在Django中使用JWT实现无状态认证?3.djangorestframework-simplejwt如何配置Token过期时间?4.如何在前端使用Django返回Token?5.自定义TokenJWTToken各自的优缺点是什么?</think>在 Django 中实现通过数据库验证用户登录并生成返回 Token 的功能,主要有两种推荐方法:自定义 Token 机制和使用第三方库(如 djangorestframework-simplejwt)。以下是具体实现步骤: --- ### 方法一:自定义 Token 机制(适合简单需求) #### 1. 创建用户模型和 Token 模型 ```python # models.py from django.db import models from django.contrib.auth.models import AbstractUser import secrets class CustomUser(AbstractUser): # 可添加自定义字段 pass class AuthToken(models.Model): user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) key = models.CharField(max_length=64, unique=True) created = models.DateTimeField(auto_now_add=True) @classmethod def generate_token(cls): return secrets.token_hex(32) # 生成64字符随机Token ``` #### 2. 实现登录视图 ```python # views.py from django.contrib.auth import authenticate from django.http import JsonResponse from .models import AuthToken def custom_login(request): if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") # 验证用户凭证 user = authenticate(username=username, password=password) if user: # 创建或获取现有Token token, created = AuthToken.objects.get_or_create(user=user) if not created: # 重新生成新Token token.key = AuthToken.generate_token() token.save() return JsonResponse({ "token": token.key, "user_id": user.id, "username": user.username }) return JsonResponse({"error": "用户名或密码错误"}, status=401) return JsonResponse({"error": "仅支持POST请求"}, status=405) ``` #### 3. 配置路由 ```python # urls.py from django.urls import path from . import views urlpatterns = [ path('login/', views.custom_login, name='login'), ] ``` --- ### 方法二:使用 JWT(推荐生产环境使用) #### 1. 安装必要库 ```bash pip install djangorestframework djangorestframework-simplejwt ``` #### 2. 配置 settings.py ```python # settings.py INSTALLED_APPS = [ ... 'rest_framework', 'rest_framework_simplejwt', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ) } # JWT配置 from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), } ``` #### 3. 实现登录视图 ```python # views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework_simplejwt.tokens import RefreshToken class JWTLoginView(APIView): def post(self, request): username = request.data.get("username") password = request.data.get("password") user = authenticate(username=username, password=password) if not user: return Response({"error": "认证失败"}, status=401) # 生成JWT Token refresh = RefreshToken.for_user(user) return Response({ "access": str(refresh.access_token), "refresh": str(refresh), "user_id": user.id }) ``` #### 4. 配置路由 ```python # urls.py from django.urls import path from .views import JWTLoginView urlpatterns = [ path('jwt-login/', JWTLoginView.as_view(), name='jwt_login'), ] ``` --- ### 客户端调用示例 ```javascript // JavaScript示例 fetch('/login/', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: `username=testuser&password=securepassword123` }) .then(response => response.json()) .then(data => { console.log("Token:", data.token); localStorage.setItem('authToken', data.token); }); ``` --- ### 关键安全实践 1. **HTTPS强制使用**:所有Token传输必须通过HTTPS 2. **Token存储安全**: ```javascript // 前端避免XSS攻击 localStorage.setItem('token', token); // 替代cookie存储 ``` 3. **设置合理有效期**: ```python # JWT配置示例 SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15), // 短有效期 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), // 长刷新周期 } ``` 4. **敏感操作二次验证**:关键操作要求重新输入密码 --- ### 方法对比 | 特性 | 自定义Token | JWT | |--------------------|--------------------------------|-------------------------------| | 实现复杂度 | 简单 | 中等 | | 无状态性 | 需数据库查询 | 无需数据库查询 | | 安全性 | 依赖实现细节 | 标准化加密 | | 自动过期 | 需手动实现 | 内置支持 | | 适用场景 | 小型项目/简单API | 分布式系统/RESTful API | > 对于新项目,**强烈推荐使用JWT方案**(方法二),它提供了开箱即用的安全功能和标准化实现[^5]。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值