JWT

jwt(json web token)

jwt基本介绍

解决什么问题?

随着技术的发展,分布式web应用的普及,通过session管理用户登录状态成本越来越高,因此慢慢发展成为token的方式做登录身份校验,然后通过token去取redis中的缓存的用户信息,随着之后jwt的出现,校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单。

token格式
三段式 - 头.载荷.签名 - 头和载荷才有的是base64可逆加密,签名才有md5不可逆加密

token内容
头(基础信息,也可以为空):加密方式、公司信息、项目组信息、...
载荷(核心信息):用户信息、过期时间、...
签名(安全保障):头加密结果+载荷加密结果+服务器秘钥 的md5加密结果

加密方式

base64是可逆的算法、hash256是不可逆的算法

安全保障

安全保障主要靠的就是签名部分,比如Django的服务器密钥就是唯一的安全保障

认证规则

前台登录-->登录成功,后台签发token-->前台保存token-->前台访问用户数据-->后台校验获取合法用户

使用jwt

主要功能

对认证模块进行封装,登录成功返回加密的token,还有解析token的功能

安装

模块包

rest_framework_jwt

pip install djangorestframework-jwt

jwt的三个主要功能

三个视图接口

但是jwt默认实现的功能过于简单,所以一般不直接使用它提供的视图接口

url(r'^login1/$', obtain_jwt_token),

#签发token,接收username和password
obtain_jwt_token = ObtainJSONWebToken.as_view()

#校验token,验证token,对的原样返回,错的报错
refresh_jwt_token = RefreshJSONWebToken.as_view()

#刷新token,使用要配置'JWT_ALLOW_REFRESH': False,允许刷新为True,token过期,刷新为有效时间
verify_jwt_token = VerifyJSONWebToken.as_view()

jwt配置

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),

    # 'JWT_ALLOW_REFRESH': True,
    # 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),

    'JWT_AUTH_HEADER_PREFIX': 'JWT',
}

签发token流程

user-->jwt_payload_handler-->payload-->jwt_encode_handler-->token
   #全局钩子
   def validate(self, attrs):
        user = authenticate(**attrs)
        if not user:
            raise ValidationError({'user':'信息有误'})
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)

        self.user = user
        self.token = token

        return attrs

注意

登录接口view设置

    authentication_classes = []
    permission_classes = []

jwt全局认证

# drf的配置
REST_FRAMEWORK = {
    # 异常模块
    'EXCEPTION_HANDLER': 'utils.exception.exception_handler',

    # 认证模块
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # jwt认证类
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ],
    # 权限模块
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.IsAuthenticated',
        # 自定义权限类
    ],
    # 频率设置
    'DEFAULT_THROTTLE_RATES': {
        'three': '3/min',
    },
}

多重登录

views.py

from rest_framework.views import APIView
from . import models, serializers
from utils.response import APIResponse
class LoginAPIView(APIView):
    authentication_classes = []
    permission_classes = []
    def post(self, request, *args, **kwargs):
        serializer = serializers.LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        return APIResponse(msg='login success', data={
            'username': serializer.user.username,
            'token': serializer.token
        })

serializers.py

from rest_framework.serializers import ModelSerializer, CharField, ValidationError, SerializerMethodField
from . import models
from django.contrib.auth import authenticate
import re
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
class LoginSerializer(ModelSerializer):
    username = CharField(write_only=True)
    password = CharField(write_only=True)
    class Meta:
        model = models.User
        fields = ('username', 'password')

    # 在全局钩子中签发token
    def validate(self, attrs):
        # user = authenticate(**attrs)
        # 账号密码登录 => 多方式登录
        user = self._many_method_login(**attrs)

        # 签发token,并将user和token存放到序列化对象中
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)

        self.user = user
        self.token = token

        return attrs

    # 多方式登录
    def _many_method_login(self, **attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        if re.match(r'.*@.*', username):
            user = models.User.objects.filter(email=username).first()  # type: models.User

        elif re.match(r'^1[3-9][0-9]{9}$', username):
            user = models.User.objects.filter(mobile=username).first()
        else:
            user = models.User.objects.filter(username=username).first()

        if not user:
            raise ValidationError({'username': '账号有误'})

        if not user.check_password(password):
            raise ValidationError({'password': '密码有误'})

        return user
posted @ 2019-11-27 23:37  zx125  阅读(90)  评论(0编辑  收藏  举报