django_restfreamwork 4 JWT认证组件

Posted on 2020-08-21 19:04  王石头py  阅读(267)  评论(0)    收藏  举报

DRF【jwt认证组件】

前后端分离项目都用token认证

前后端不分离的项目都用cookie和session认证

通用配置

总路由

from django.urls import path, include
import xadmin
xadmin.autodiscover()
# version模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()

urlpatterns = [
    # path('admin/', admin.site.urls),
    path(r'xadmin/', xadmin.site.urls),
    path('books/', include("SerDemo.urls")),
]

模型

from django.db import models

__all__ = ["Book", "Publisher", "Author"]

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="图书名称")
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别")
    pub_time = models.DateField(verbose_name="图书的出版日期")

    publisher = models.ForeignKey(to="Publisher", on_delete=None)
    author = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title

    class Meta:
        db_table = "book"
        verbose_name_plural = "图书表"

class Publisher(models.Model):
    title = models.CharField(max_length=32, verbose_name="出版社的名称")

    def __str__(self):
        return self.title

    class Meta:
        db_table = "publisher"
        verbose_name_plural = "出版社表"

class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者的姓名")

    def __str__(self):
        return self.name

    class Meta:
        db_table = "author"
        verbose_name_plural = "作者表"

认证准备工作

新建子应用user

python3 manage.py startapp user

注册子应用

INSTALLED_APPS = [
  	...
    ...
		...
    #子应用
    'user',
]

创建用户模型类models.py

user/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    """用户模型类"""
    # 如果手机设置 IntegerField的话,只有10位数字,并且有正有负
    # 手机号不需要计算,所以不需要设置int类型,设置字符串即可
    mobile = models.CharField(max_length=15, unique=True, verbose_name='手机号')
    avatar = models.ImageField(upload_to="avator",null=True,blank=True,verbose_name="用户头像")
    class Meta:
        db_table = 'users'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

注册用户模型类

自定义的用户模型类还不能直接被Django的认证系统所识别,需要在配置文件中告知Django认证系统使用自定义的模型类。

settings.py

#注册自定义user模型 --> '子应用名.models类名'
AUTH_USER_MODEL = 'user.User'

数据库迁移

python3 manage.py makemigrations
python3 manage.py migrate

若报错,解决方案

如果在第一次数据迁移以后,才设置 AUTH_USER_MODEL 自定义用户模型,则会报错。解决方案如下:

0. 先把现有的数据库导出备份,然后清掉数据库 中 所有的数据表,不删库,只删除下面所有的表。
1. 把开发者创建的所有子应用apps/下面的migrations目录下除了__init__.py以外的所有迁移文件,只要涉及到用户的,一律删除
2. 把django.contrib.admin.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。
3. 把django.contrib.auth.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。
4. 把reversion.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。
5. 把xadmin.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。
6. 接下来,重新执行数据迁移,回顾第0步中的数据,以后如果要修改用户相关数据,不需要重复本次操作,直接数据迁移即可。

python manage.py makemigrations
python manage.py migrate

7. 录入备份的数据
解决步骤0-6

录入备份数据

  • 录入之前先迁移数据库,先新建表之后在录入数据

重新创建xadmin超级用户

python3 manage.py createsuperuser
用户名 (leave blank to use 'wangjunxiang'): root
电子邮件地址: root@root.com
Password: 123123
Password (again): 123123
密码长度太短。密码必须包含至少 8 个字符。
这个密码太常见了。
密码只包含数字。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

实现JWT认证

安装&配置 JWT

pip3 install djangorestframework-jwt -i https://pypi.tuna.tsinghua.edu.cn/simple

settings.py

REST_FRAMEWORK = {
    #jwt登录认证
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

# 设置jwt token认证有效期
import datetime
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),#可以设置seconds minutes hours days weeks
}

总路由分发子路由

urls.py

urlpatterns = [
		...
    path('user/', include('user.urls')),
    # include 的值必须是 模块名.urls 格式,字符串中间只能出现一个圆点
]

新建子路由

新建文件 user/urls.py

from rest_framework_jwt.views import obtain_jwt_token
from django.urls import path
urlpatterns = [
    path(r'login/', obtain_jwt_token),
]

测试认证

如果什么都不带 直接发送 127.0.0.1:8000/user/login/ 的 post 请求,会返回

如果发送post请求 带如下参数 ,则返回token,需要前端将token存在浏览器中,这样下次继续访问,会带着这个token 和其他参数去访问,后端先接收到token验证成功后,就会转给对应的接口响应需要的数据

这个是之前创建超级用户的账号和密码

{
	"username": "root",
	"password": "123123"
}

设置jwt返回自定义数据

jwt返回自定义数据utils.py

新建 user/utils.py 作为 user通用的

  • 当然如果公司有其他需求,需要返回用户头像等其他的数据
  • 修改这个函数的返回值即可,比如返回 数据库中用户头像的url字段
  • 返回值全都自定义
def jwt_response_payload_handler(token, user=None, request=None):
    """
    自定义jwt认证成功返回数据
    token:本次登录成功后,返回的jwt数据
    user:本次登陆后,从数据库查询到的用户模型信息
    request:本次客户端的请求对象,
    """
    return {
        # token 就是前段请求 生成 保存在客户端的的token数据
        'token': token,
        # user.id 和 user.username 都是从user用户数据库模型中的数据库.字段获取
        'id': user.id,
        'username': user.username
    }

配置jwt返回自定义数据生效

settings/dev.py

在 JWT_AUTH 中新增

#JWT_AUTH = {
    ....
  	....
    # 设置返回数据的格式
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',
#}

测试返回数据

同样携带json的账户和密码信息 post 请求 127.0.0.1:8000/user/login/

{
	"username": "root",
	"password": "123123"
}

可以返回自定义数据

jwt的多条件登录

例如手机号登录

思路

  • JWT扩展的登录视图,在收到用户名与密码时,也是调用Django的认证系统中提供的authenticate()来检查用户名与密码是否正确。

  • 可以通过修改Django认证系统的认证后端(主要是authenticate方法)来支持登录账号既可以是用户名也可以是手机号。

修改Django认证系统的认证后端需要继承django.contrib.auth.backends.ModelBackend,并重写authenticate方法

authenticate(self, request, username=None, password=None, **kwargs)
方法的参数说明:
- request 本次认证的请求对象
- username 本次认证提供的用户账号
- password 本次认证提供的密码

想让用户既可以 用户名登录,也可以 手机号登录,那么对于authenticate方法而言,前端既可以把用户名或者手机号传后端验证,但是都存在username变量中,也就是username有可能是用户名,也有可能是手机号

重写authenticate方法的思路

  • 根据username参数查找用户User对象,username参数可能是用户名,也可能是手机号

  • 若查找到User对象,调用User对象的check_password方法检查密码是否正确

新增自定义认证方法utils.py

users/utils.py中新增:

# def jwt_response_payload_handler(token, user=None, request=None):
#     """
#     自定义jwt认证成功返回数据
#     token:本次登录成功后,返回的jwt数据
#     user:本次登陆后,从数据库查询到的用户模型信息
#     request:本次客户端的请求对象,
#     """
#     return {
#         # token 就是前段请求 生成 保存在客户端的的token数据
#         'token': token,
#         # user.id 和 user.username 都是从user用户数据库模型中的数据库.字段获取
#         'id': user.id,
#         'username': user.username
#     }
# 

#如果还要加用邮箱登录吧mobile改为email就行
from .models import User
from django.db.models import Q      #Q对象用来做多条件查询
from django.contrib.auth.backends import ModelBackend
def get_user_by_account(account):
    """
    根据帐号获取user对象
    :param account: 账号,可以是用户名username,也可以是手机号mobile,或者其他的数据
    :return: User对象 或者 None
    """
    try:

        # Q对象多条件查询, '|' 代表或者
        user = User.objects.filter( Q(username=account) | Q(mobile=account)).first()
    except User.DoesNotExist:
        return None
    else:
        return user

class UsernameMobileAuthBackend(ModelBackend):	#继承ModelBackend
    """ 
    	自定义用户名或手机号认证
    """
    def authenticate(self, request, username=None, password=None, **kwargs):
        user = get_user_by_account(username)
        # user模型下有check_password方法比较 ,user.is_authenticated用来验证权限
        if user is not None and user.check_password(password) and user.is_authenticated:
            return user
        else:
            return None

注册自定义认证方式生效

settings.py

AUTHENTICATION_BACKENDS = [
    'user.utils.UsernameMobileAuthBackend',
]

测试多条件登录

之前是post请求 127.0.0.1:8000/user/login/ 接口

携带username 和 password,现在将username的root 换为了手机号,同样可以返回token