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

使用JWT实现Django经销商账户身份认证的可行方案求助

经销商JWT身份认证落地方案

注意:你当前Distributors模型中password字段为明文存储,存在严重安全隐患,方案第一步会优先改造密码存储逻辑。


1. 改造Distributors模型,新增密码加解密方法

修改后的模型代码如下:

from django.contrib.auth.hashers import make_password, check_password
from django.db import models
# 你的其他依赖导入如TimeStampedModel、BRCPFField等

class Distributors(TimeStampedModel):
    name = models.CharField("Nome completo", max_length=250)
    cpf = BRCPFField("CPF")
    email = models.EmailField("Email")
    # 原长度15无法存储加密后的密码,修改为128位
    password = models.CharField("Senha", max_length=128)

    def __str__(self):
        return self.name
    
    # 新增:密码加密存储方法,创建经销商账号时必须调用
    def set_password(self, raw_password):
        self.password = make_password(raw_password)
    
    # 新增:密码校验方法,登录逻辑会调用
    def check_password(self, raw_password):
        return check_password(raw_password, self.password)

修改完成后执行Django迁移命令同步表结构,存量明文密码需要批量调用set_password方法完成加密。

正确创建经销商账号示例:

distributor = Distributors(
    name="测试经销商",
    cpf="对应CPF编码",
    email="xxx@test.com"
)
distributor.set_password("用户设置的明文密码") # 必须调用该方法加密密码
distributor.save()

2. 自定义经销商认证后端

在APP目录下新建backends.py文件,适配Distributors表的认证逻辑:

from django.contrib.auth.backends import BaseBackend
from .models import Distributors

class DistributorAuthBackend(BaseBackend):
    def authenticate(self, request, name=None, password=None, **kwargs):
        if not name or not password:
            return None
        try:
            distributor = Distributors.objects.get(name=name)
        except Distributors.DoesNotExist:
            return None
        # 校验密码
        if distributor.check_password(password):
            return distributor
        return None
    
    def get_user(self, user_id):
        try:
            return Distributors.objects.get(pk=user_id)
        except Distributors.DoesNotExist:
            return None

修改项目settings.py,新增认证后端配置:

AUTHENTICATION_BACKENDS = [
    # 保留默认Django用户认证后端,不影响原有超级用户的登录逻辑
    'django.contrib.auth.backends.ModelBackend',
    # 新增经销商认证后端,替换为你自己的实际文件路径
    '你的APP名称.backends.DistributorAuthBackend',
]

3. 自定义JWT登录视图,适配name+password校验规则

本方案基于djangorestframework-simplejwt(Django生态最常用的JWT库)实现,自定义登录序列化器和视图:

from rest_framework import serializers
from rest_framework_simplejwt.views import TokenObtainPairView
from .backends import DistributorAuthBackend

class DistributorTokenSerializer(serializers.Serializer):
    name = serializers.CharField(required=True)
    password = serializers.CharField(required=True, write_only=True)

    def validate(self, attrs):
        name = attrs.get('name')
        password = attrs.get('password')
        # 调用自定义认证后端校验身份
        distributor = DistributorAuthBackend().authenticate(
            request=self.context.get('request'), 
            name=name, 
            password=password
        )
        if not distributor:
            raise serializers.ValidationError('用户名或密码错误')
        # 生成JWT token
        refresh = TokenObtainPairView.get_token(distributor)
        return {
            'refresh': str(refresh),
            'access': str(refresh.access_token),
            'distributor_name': distributor.name,
            'email': distributor.email
        }

class DistributorTokenObtainView(TokenObtainPairView):
    serializer_class = DistributorTokenSerializer

4. 配置登录路由

修改APP的urls.py,新增经销商登录接口路由:

from django.urls import path
from .views import DistributorTokenObtainView

urlpatterns = [
    # 经销商登录获取token接口
    path('api/distributor/login/', DistributorTokenObtainView.as_view(), name='distributor_login'),
    # 其他原有路由
]

5. 接口权限校验配置

修改项目settings.py,配置REST框架默认认证类:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}

后续需要校验经销商登录状态的接口,只需添加权限类permission_classes = [IsAuthenticated],即可正常校验身份,request.user会直接返回对应的Distributors对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:54:04