JWT的简单使用


安装模块 jwt是一个第三方模块

pip install djangorestframework-jwt -i https://pypi.douban.com/simple/

JWT的使用 (一) 自定义返回的

settings.py 里面配置一下

REST_FRAMEWORK = {
    # 引入JWT认证机制,当客户端将jwt token传递给服务器之后
    # 此认证机制会自动校验jwt token的有效性,无效会直接返回401(未认证错误)
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
    'PAGE_SIZE': 100,  # 显示多少数据
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 防止跳出 警告
}

# JWT扩展配置
JWT_AUTH = {
    # 重新定义jwt认证成功后返回的数据 days(天) hours(时) minutes(分)
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),  # 定义token过期时间1天
}

手动生成 JWT token

该方法 在后面会用!!!

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)

子应用---模型类---views.py

models.py

from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    """自定义用户模型类"""
    mobile = models.CharField(max_length=11, unique=True, verbose_name='Mobile')

    class Meta:
        db_table = 'tb_users'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username

子应用里的 urls.py

# coding:utf-8
from django.conf.urls import url

from apps.user.views import *

app_name = 'user'
urlpatterns = [
    url(r'^register/$', RegisterView.as_view()),  # 用户注册接口
    url(r'^mobiles/$', MobileCountView.as_view()),  # 校验手机号码是否注册过
    url(r'^login/$', AdminAuthorizeView.as_view()),  # 用户jwt认证接口
]

views.py

# coding:utf-8
import re

from django import http
from django.contrib.auth import authenticate, login
from django.views import View
from django_redis import get_redis_connection
from pymysql import DatabaseError
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.settings import api_settings

from apps.user.models import User
from utils.response_code import RET


class AdminAuthorizeView(APIView):
    def post(self, request):
        """
        登录
        :param request:
        :return:
        """
        username = request.POST.get('username')
        password = request.POST.get('password')

        if username is None or password is None:
            return Response({'code': 500, 'message': '请求参数错误'})

        is_login = authenticate(request, username=username, password=password)
        if is_login is None:
            return Response({'code': 500, 'message': '账号或密码错误'})

        login(request, is_login) # 保存登录状态 但没必要 因为已经是 jwt认证了
        ### 此处为生成 token 的地方 ###
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(is_login)
        token = jwt_encode_handler(payload)
        return Response({'code': RET.OK, 'message': "登录成功", "token": token})


class RegisterView(APIView):
    def post(self, request):
        """
        注册
        :param request:
        :return:
        """
        password = request.POST.get('password')
        mobile = request.POST.get('mobile')
        # 短信验证码
        # sms_code = request.POST.get('sms_code')

        # 判断参数是否齐全
        if not all([password, mobile]):
            return http.HttpResponseBadRequest('缺少必传参数')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseBadRequest('请输入8-20位的密码')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseBadRequest('请输入正确的手机号码')

        # redis_conn = get_redis_connection('code')
        # sms_code_saved = redis_conn.get('sms_%s' % mobile)
        # if sms_code_saved is None:
        #    return Response({'code': RET.NODATA, 'errmsg': '无效的验证码'})
        # if sms_code != sms_code_saved.decode():
        #    return Response({'code': RET.DATAERR, 'errmsg': '验证码输入错误'})

        try:
            User.objects.create_user(username=mobile, password=password, mobile=mobile)
        except DatabaseError:
            return Response({'code': RET.DBERR, 'message': '注册失败'})

        return Response({'code': RET.OK, 'message': "完成注册"})


class MobileCountView(View):
    def get(self, request):
        """
        校验手机号码是否重复注册
        :param request:
        :return:
        """
        mobile = request.GET.get('mobile')
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({'code': RET.OK, 'count': count})

前端返回token后,后端校验token

安装cors

pip install django-cors-headers

前后端分离 会有跨域的问题 并且 没有使用到 COOKIES SESSIONS

settings.py文件里面 配置跨域

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'apps.user',  # user
    'corsheaders',  # 跨域
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # 跨域
    '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',
]

# 跨域增加忽略
CORS_ALLOW_CREDENTIALS = True  # 允许携带cookie
CORS_ORIGIN_ALLOW_ALL = True
# 设置允许的请求
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)
# 设置允许的header
CORS_ALLOW_HEADERS = (
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'Pragma',
)

自定义基于jwt的权限类 (第一种方法)

新建一个auth.py 用于认证用户是否登录 在一些需要登录后才能访问的视图做校验

from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework.exceptions import AuthenticationFailed
import jwt
from apps.user.models import User

class MyBaseAuthentication(BaseAuthentication):
    # 重写authenticate
    def authenticate(self, request):
        # 获取token的第二部分
        jwt_value = request.META.get('HTTP_AUTHORIZATION')
        # 注意此处为 postman 的时候请求 会带有 Bearer 所以使用了切割字符串
        jwt_value = jwt_value.split()[1]  # 浏览器请求头上传的时候 可以将此句注释掉
        if not jwt_value:
            # 如果没有就抛异常
            raise AuthenticationFailed('您没有携带认证信息')
        try:
            # 将token反解成用户信息字典
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            raise AuthenticationFailed('认证超时')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('非法用户')
        except Exception as e:
            raise AuthenticationFailed(str(e))
        # 直接返回对象 不查库,速度快些(只能获取传入的参数的值)
        # user = User(id=payload.get('user_id'), username=payload.get('username'))
        # 去数据库中查找获取user对象(能获取的字段更多)
        user = User.objects.filter(pk=payload.get('user_id')).first()
        return user, jwt_value

自定义基于jwt的权限类 (第二种方法)

from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework.exceptions import AuthenticationFailed
import jwt

# 继承BaseJSONWebTokenAuthentication,重写父类的authenticate方法
class MyToken(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        # 获取token的第二部分
        jwt_value = request.META.get('HTTP_AUTHORIZATION')
        print(jwt_value)
        # 判断get请求头中是否携带token,如果没有就是游客模式,直接返回None

        if jwt_value is None:
            return None
        try:
            # 将token数据进行decode解码后得到payload(用户信息字典),如果token超时或者错误,就会抛出异常
            payload = jwt_decode_handler(jwt_value)
            # token = jwt_encode_handler(payload) 获取token
        except jwt.ExpiredSignature:
            raise AuthenticationFailed('认证超时')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('非法用户')
        except Exception as e:
            raise AuthenticationFailed(str(e))
        # 将用户信息字典传入,内部会查询数据库返回user对象
        user = self.authenticate_credentials(payload)
        # 最后返回两个参数,一个是user对象,另一个是token(也可以是其他数据看你自己要返回啥)
        return user, jwt_value

使用POSTman 测试

class SearchCateGoryView(APIView):
    # authentication_classes = [MyToken, ]
    ### 自定义认证 JWT 方法
    authentication_classes = [MyBaseAuthentication, ]
    def get(self,request):
        data = 'get方法'
        return Response({"code":"OK",'message':data})

文章作者: 柒仔
文章链接: /article/1/
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 XiaoLiu!
侵权声明: 若无意对您的文章造成侵权,请您留言,博主看到后会及时处理,谢谢。
评论-----昵称和邮箱必填,网址选填
  目录