Django--012 JWT和注册登陆

1. JWT

1.1 简介

  • 定义:JSON Web Token,目前最流行的跨域身份验证解决方案
  • 原理: 服务器身份验证之后,将生成一个JSON对象并将其发送回用户。当用户与服务器进行后续通信时,客户在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据,服务器将在生成对象时添加签名。
  • 优点: 服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展
  • 构成: header、playload、signature
    • header:声明类型、加密算法(默认HS256)、base64加密(可解密)
    • playload:存放过期时间、用户的非敏感信息、base64加密(可解密)
    • signature:base64加密后的header + base64加密后的playload + (HS256+secret)加密串

1.2 使用

  • 安装jwt模块
pip install pyjwt
  • 使用
import jwt

# 第一部分header,不需要指定

# 第二部分payload
payload = {
    'username': 'zhangsan',
    'password': '123456'
}

# 服务端创建token
token = jwt.encode(payload, key='666')
print(token)


# 服务端解密token
# algorithm:加密算法,字符串类型
data = jwt.decode(token, key='666', algorithms='HS256')
print(data)

2. DRF-jwt

2.1 django-user源码

  • Django 自带认证功能auth模块和User对象的基本操作
  • user模型需继承django自带的用户认证系统AbstractUser才可以使用
  • django
# /site-packages/django/contrib/auth/models.py

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=150, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)


class User(AbstractUser):
    """
    Users within the Django authentication system are represented by this
    model.

    Username and password are required. Other fields are optional.
    """
    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'

2.2 登陆功能

  • DRF具有完善的登陆功能,只需新建user子应用,并注册子应用、配置url、修改JWT认证方式即可
  • 库安装
pip install djangorestframework_jwt 
  • url配置
# user/urls.py

from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token


urlpatterns = [
    path('login/', obtain_jwt_token)
]
# dev/urls.py

urlpatterns = [
 	...
    path('user/', include('user.urls')),
]
  • settings配置
# dev/settings.py

# DRF配置信息
REST_FRAMEWORK = {
	...
    # 认证类
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # JWT认证
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        # 会话认证
        'rest_framework.authentication.SessionAuthentication',
        # 基本认证(用户名/密码)
        'rest_framework.authentication.BasicAuthentication'
    ],
    # 权限类
    'DEFAULT_PERMISSION_CLASSES': [
        # 不需要登录就拥有任意权限
        # 'rest_framework.permissions.AllowAny',
        # # 只要登录后,就拥有任意权限
        'rest_framework.permissions.IsAuthenticated',
        # # 仅管理员用户才有权限
        # 'rest_framework.permissions.IsAdminUser',
        # 未认证成功只有只读权限,认证成功户拥有任意权限
        # 'rest_framework.permissions.IsAuthenticatedOrReadOnly'
    ],
}
  • 访问

使用token访问时,要在header加上 Authorization:"JWT token"

# 登陆,获取token
http :8000/user/login/ username=zy password=123456

http :8000/projects/ Authorization:"JWT token"
# 或者
http -a zy:123456 :8000/projects/ 

2.3 expires和token设置

  • 自定义token返回处理器
  • 源码路径:site-packages/rest_framework_jwt/utils.py
# utils/jwt_handler.py

def jwt_response_payload_handler(token, user=None, request=None):
    """
    Returns the response data for both the login and refresh views.
    Override to return a custom response such as including the
    serialized representation of the User.

    Example:

    def jwt_response_payload_handler(token, user=None, request=None):
        return {
            'token': token,
            'user': UserSerializer(user, context={'request': request}).data
        }

    """
    return {
        'token': token,
        'username': user.username,
        'user_id': user.id
    }
  • 配置
# settings.py

import datetime

JWT_AUTH = {
	# 设置token过期时间为1天
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    # 修改JWT前缀,默认JWT
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
    # 修改payload函数,取自定义
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'utils.jwt_handler.jwt_response_payload_handler',
}

3. 注册功能

3.1 注册需求

参数输入/出校验/描述
用户名输入/出6-20位,不能重复
邮箱输入合法格式,不能重复
密码输入6-20位,与确认密码一致
确认密码输入6-20位,与确认密码一致
token输入注册成功之后返回token

3.2 代码

from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework.validators import UniqueValidator
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler


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

    class Meta:
        model = User
        fields = ('id', 'username', 'password', 'email', 'password_confirm', 'token')
        extra_kwargs = {
            'username': {
                'label': '用户名',
                'help_text': '用户名',
                'min_length': 6,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许6-20个字符的用户名',
                    'max_length': '仅允许6-20个字符的用户名',
                }
            },
            'email': {
                'label': '邮箱',
                'help_text': '邮箱',
                'write_only': True,
                # 前端必传
                'required': True,
                # 添加邮箱重复校验
                'validators': [UniqueValidator(queryset=User.objects.all(), message='此邮箱已注册')],
            },
            'password': {
                'label': '密码',
                'help_text': '密码',
                'write_only': True,
                'min_length': 6,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许6-20个字符的密码',
                    'max_length': '仅允许6-20个字符的密码',
                }
            }
        }

    def validate(self, attrs):
        if attrs.get('password') != attrs.get('password_confirm'):
            raise serializers.ValidationError('密码与确认密码不一致')
        return attrs

    def create(self, validated_data):
        validated_data.pop('password_confirm')
        # 创建user模型对象
        user = User.objects.create_user(**validated_data)

        # jwt_payload_handler--对payload加密
        payload = jwt_payload_handler(user)
        # jwt_encode_handler--对三个部分加密生成token
        token = jwt_encode_handler(payload)

        user.token = token
        return user

# user/urls.py

from django.urls import path, re_path
from rest_framework_jwt.views import obtain_jwt_token
from . import views


urlpatterns = [
    path('login/', obtain_jwt_token),
    path('register/', views.RegisterView.as_view()),
    re_path(r'^(?P<username>\w{6,20})/count/$', views.UsernameIsExistedView.as_view()),
    re_path(r'^(?P<email>[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+)/count/$',
            views.EmailIsExistedView.as_view())
]

# user/views.py

from django.contrib.auth.models import User
from rest_framework.generics import CreateAPIView
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import permissions
from . import serializers


# class RegisterView(CreateAPIView):
#     serializer_class = serializers.RegisterSerializer
#     permission_classes = [permissions.AllowAny]


# 注册
class RegisterView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request, *args, **kwargs):
        serializer = serializers.RegisterSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)


# 获取用户名个数
class UsernameIsExistedView(APIView):
    permission_classes = [permissions.AllowAny]

    def get(self, request, username):
        count = User.objects.filter(username=username).count()
        data_dict = {
            'username': username,
            'count': count
        }
        return Response(data_dict, status=status.HTTP_200_OK)


# 获取邮箱个数
class EmailIsExistedView(APIView):
    permission_classes = [permissions.AllowAny]

    def get(self, request, email):
        data_dict = {
            'email': email,
            'count': User.objects.filter(email=email).count()
        }
        return Response(data_dict, status=status.HTTP_200_OK)

4. 重写user-models

  • 模型类必须继承自AbstractUser
# user/models.py

class User(AbstractUser):
	...
  • 必须全局指定自定义的模型
# settings.py

AUTH_USER_MODEL = 'app.user'
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值