Django+React登录功能遇401 Unauthorized及405 Method Not Allowed错误求助
先解决直观的405 Method Not Allowed错误
你访问GET /api/farmer/login/时出现这个错误,原因其实很明确:你的Django视图FarmerLoginView只定义了post方法,接口返回的Allow: POST, OPTIONS也直接说明了——这个登录接口只接受POST请求,不支持GET请求。
浏览器地址栏访问默认是GET方法,所以直接输地址肯定会报错。正常的登录流程应该是通过React表单提交POST请求,这部分你代码里的POST逻辑是对的,只要别用GET访问这个接口就没问题。
重点排查401 Unauthorized错误(登录失败核心问题)
这个错误是因为你的登录认证逻辑存在几个关键漏洞,导致服务器认为凭证无效,返回401:
1. 密码未正确哈希存储
你的Farmer和Customer模型里的set_password方法只是直接把明文密码赋值给字段:
def set_password(self, password): self.password = password
但Django的authenticate函数默认只认哈希后的密码,如果注册时你直接把明文密码存进数据库,authenticate会匹配失败,返回None,进而触发401错误。
修复方法:修改模型的set_password方法,用Django内置的make_password工具哈希密码:
from django.contrib.auth.hashers import make_password def set_password(self, password): self.password = make_password(password)
同时要确保你在创建用户(注册)时,是调用set_password方法设置密码,而不是直接给password字段赋值。
2. 缺少自定义用户认证后端
你的视图里用authenticate(request=request, email=farmer.email, password=password)来验证,但Django默认的认证后端是基于username字段的,而你的Farmer和Customer模型并没有username字段,只有email和phone_number。
默认的authenticate函数根本识别不了email这个参数,会直接返回None。你需要自定义一个认证后端,支持通过邮箱或手机号登录:
先在你的app下创建backends.py文件:
from django.contrib.auth.backends import BaseBackend from .models import Farmer, Customer class FarmerAuthBackend(BaseBackend): def authenticate(self, request, email_or_mobile=None, password=None, **kwargs): # 先尝试通过邮箱找Farmer try: farmer = Farmer.objects.get(email=email_or_mobile) except Farmer.DoesNotExist: # 再尝试通过手机号找 try: farmer = Farmer.objects.get(phone_number=email_or_mobile) except Farmer.DoesNotExist: return None # 验证密码(用check_password自动匹配哈希值) if farmer.check_password(password): return farmer def get_user(self, user_id): try: return Farmer.objects.get(pk=user_id) except Farmer.DoesNotExist: return None # 同理创建Customer的认证后端 class CustomerAuthBackend(BaseBackend): def authenticate(self, request, email_or_mobile=None, password=None, **kwargs): try: customer = Customer.objects.get(email=email_or_mobile) except Customer.DoesNotExist: try: customer = Customer.objects.get(phone_number=email_or_mobile) except Customer.DoesNotExist: return None if customer.check_password(password): return customer def get_user(self, user_id): try: return Customer.objects.get(pk=user_id) except Customer.DoesNotExist: return None
然后在settings.py里配置认证后端:
AUTHENTICATION_BACKENDS = [ 'yourapp.backends.FarmerAuthBackend', 'yourapp.backends.CustomerAuthBackend', # 保留默认后端可选,根据你的需求调整 'django.contrib.auth.backends.ModelBackend', ]
之后修改视图里的authenticate调用,改为传入email_or_mobile参数,而不是email:
# FarmerLoginView里的代码 farmer_user = authenticate(request=request, email_or_mobile=email_or_mobile, password=password)
这样就能正确触发自定义后端的认证逻辑了。
3. React表单存在重复提交问题
你的React表单里,form标签绑定了onSubmit={handleSubmit},同时两个按钮又绑定了onClick={(event) => handleSubmit(event, 'farmer')},这会导致点击按钮时,表单的onSubmit和按钮的onClick会同时触发,可能导致重复请求或者数据异常。
修复方法:移除form的onSubmit绑定,只保留按钮的onClick:
<form>
这样就不会重复触发请求了。
4. 序列化器未被利用(可选优化)
你写了UserLoginSerializer但在视图里并没有用上,而是手动处理request.data,可以考虑用序列化器来统一验证数据,减少重复代码:
修改FarmerLoginView的post方法:
def post(self, request): serializer = UserLoginSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): user = serializer.validated_data['user'] login(request, user) return Response({'message': 'Login successful'}, status=status.HTTP_200_OK) return Response({'message': 'Invalid login credentials'}, status=status.HTTP_401_UNAUTHORIZED)
这样能让代码更简洁,也能统一验证逻辑。
总结修复步骤
- 确保用户密码在注册时是哈希存储的,修改模型的
set_password方法。 - 自定义认证后端,支持邮箱/手机号登录,并在settings中配置。
- 修正视图中
authenticate的参数,使用自定义后端的验证字段。 - 修复React表单的重复提交问题。
按照这些步骤调整后,应该就能解决401错误,同时405错误只要不使用GET访问登录接口就不会出现了。
备注:内容来源于stack exchange,提问作者TechLover

