django+simpleJWT实现自定义登录注册以及中间件

1.安装
首次使用需要安装django-rest-framework及jwt相关包

pip install djangorestframework
pip install djangorestframework-simplejwt

2.settings文件增加配置

INSTALLED_APPS = [
    'rest_framework',
    'rest_framework_simplejwt'
]
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),  # 全局默认配置过滤
    'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_simplejwt.authentication.JWTAuthentication',],
    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': datetime.timedelta(hours=12),
    'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=30),
    'USER_ID_FIELD': "person_id",
    # 此处id为生成token时使用自定义的用户表时会使用到,USER_ID_FIELD 是自定义用户表的id,不是id时 会报错user_id = getattr(user, api_settings.USER_ID_FIELD)
    'SIGNING_KEY': JWT_SECRET_KEY,
    'JWT_PUBLIC_KEY': JWT_SECRET_KEY,
    'JWT_PAYLOAD_HANDLER': "person_name"
}

3.登录认证
1)在serializers中添加

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework import exceptions

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    """
    自定义登录认证,使用自有用户表
    """
    username_field = 'person_name'

    def validate(self, attrs):
        password = ts.md5_encryption(attrs['password'])
        authenticate_kwargs = {self.username_field: attrs[self.username_field], 'password': password}
        try:
            user = Person_info.objects.get(**authenticate_kwargs)
        except Exception as e:
            raise exceptions.NotFound(e.args[0])
        refresh = self.get_token(user)
        refresh["name"] = user.person_name

        data = {"user_id": user.person_id, "token": str(refresh.access_token), "refresh": str(refresh)}
        return data

2)models中添加

class Person_info(models.Model):
    person_id = models.AutoField(primary_key=True)
    person_name = models.CharField(verbose_name='姓名', max_length=32, unique=True)
    password = models.CharField(verbose_name='密码', max_length=32, unique=True)

    @property
    def is_authenticated(self):
        """
        Always return True. This is a way to tell if the user has been
        authenticated in templates.
        """
        return True

    class Meta:
        verbose_name = '个人信息'
        verbose_name_plural = verbose_name
        db_table = 'person_info'

3)在views中添加

from rest_framework_simplejwt.views import TokenObtainPairView


class FormulaTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

4)url中配置

from django.contrib import admin
from django.urls import path,include
from bffile_data.views import FormulaTokenObtainPairView
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', FormulaTokenObtainPairView.as_view(), name='login')
]

ps1:如果想在自定义登录中间件,加上如下
1.在settings中添加配置

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'bffile_data.my_middleware.ExceptionChange'
]

2.新建my_middleware中定义

class ExceptionChange:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_template_response(self, request, response):
        if hasattr(response, 'data'):
            data = response.data
            if isinstance(data, dict) is True:
                if "detail" in data.keys():
                    # 用户名或密码错误
                    if data.get("detail") == "Person_info matching query does not exist.":
                        del response.data["detail"]
                        response.data["code"] = 402
                        response.data["msg"] = "用户名或者密码错误"

                    # 验证信息过期 token 过期
                    elif data.get("code") == "token_not_valid":
                        del response.data["detail"]
                        del response.data["messages"]
                        response.data["code"] = 401
                        response.data["msg"] = "登录已过期,请重新登录"

                    else:
                        del response.data["detail"]
                        response.data["code"] = 401
                        response.data["msg"] = "登录已过期,请重新登录"
        return response

ps2:注册
1.在serializers中添加

class RegisterSerializer(serializers.ModelSerializer):
    password_confirm = serializers.CharField(label='确认密码', help_text='确认密码',
                                             min_length=6, max_length=20,
                                             write_only=True,
                                             error_messages={
                                                 'min_length': '仅允许6~20个字符的确认密码',
                                                 'max_length': '仅允许6~20个字符的确认密码', })

    class Meta:
        model = Person_info
        fields = ('person_name', 'password', 'password_confirm')
        extra_kwargs = {
            'person_name': {
                'label': '用户名',
                'help_text': '用户名',
                'min_length': 6,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许6-20个字符的用户名',
                    'max_length': '仅允许6-20个字符的用户名',
                }
            },
            'password': {
                'label': '密码',
                'help_text': '密码',
                'write_only': True,
                'min_length': 6,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许6-20个字符的密码',
                    'max_length': '仅允许6-20个字符的密码',
                }
            }
        }

    # 多字段校验:直接使用validate,但是必须返回attrs
    def validate(self, attrs):
        if attrs.get('password') != attrs.get('password_confirm'):
            raise serializers.ValidationError('密码与确认密码不一致')
        user = Person_info.objects.filter(person_name=attrs.get('person_name')).first()
        if user:
            raise serializers.ValidationError('用户已经存在')
        return attrs

2.新建tools文件定义公共方法

class Create_token(TokenObtainPairSerializer):
    def validate(self, user):
        refresh = self.get_token(user)
        refresh["name"] = user.person_name
        data = {"user_id": user.person_id, "token": str(refresh.access_token), "refresh": str(refresh)}
        return data

3.在views中定义

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class UserView(APIView):
    def post(self, request):
        ser_obj = RegisterSerializer(data=request.data)
        if not ser_obj.is_valid():
            result = ts.result_data(status.HTTP_200_OK, None, "用户已注册或用户名或密码不合法")
            return Response(result)
        password = ts.md5_encryption(request.data.get("password"))
        user = models.Person_info.objects.create(person_name=request.data.get("person_name"),password=password)
        token = ts.Create_token().validate(user)
        result = ts.result_data(status.HTTP_200_OK, token, "注册成功")
        return Response(result)```
4.在urls中定义

```python
from django.contrib import admin
from django.urls import path,include
from bffile_data.views import UserView
urlpatterns = [
    path('admin/', admin.site.urls),
    path('register/', UserView.as_view(), name='register')
]

你可能感兴趣的:(restframework,python,django,mysql,python,django,中间件,restful)