0
点赞
收藏
分享

微信扫一扫

jwt的封装---1.71版本---在drf中使用

吃面多放酱 2022-03-11 阅读 37

1. 安装

pip3 install pyjwt==1.7.1

2. 签发token

import jwt
import datetime
from jwt import exceptions
from django.conf import settings


def create_token(user, day=14, SALT=settings.SECRET_KEY):
    """
    :param user: 用户对象2
    :param day:  日期。单位天 ,默认14天
    :param key:  密钥,用于加密/解密第三段
    :return:     生成的token
    """
    # 构造header
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    # 构造payload,根据需要自定义用户内容
    payload = {
        'user_id': user.id,  # 自定义用户ID
        'username': user.username,  # 自定义用户名
        'exp': datetime.datetime.utcnow() + datetime.timedelta(days=day)  # 默认14天有效
    }
    # 密钥
    SALT = SALT
    token = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
    return token

3. 使用(基于drf)

3.1 重写认证类

import jwt
from jwt import exceptions
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from app03 import models
from django.conf import settings


class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 密钥,必须跟签发token的一样
        salt = settings.SECRET_KEY
        # 从请求头中获取token
        # 放的格式 Authorization:JWT xxxxxxxx
        token = request.META.get('HTTP_AUTHORIZATION')
        if not token:
            raise AuthenticationFailed('没有携带token')
        try:
            # jwt提供了通过三段token,取出payload的方法,并且有校验功能
            # 这个是我们签发时,封装的payload字典
            verified_payload = jwt.decode(jwt=token, key=salt, verify=True)
        except exceptions.ExpiredSignatureError:
            raise AuthenticationFailed('token已失效')
        except jwt.DecodeError:
            raise AuthenticationFailed('token认证失败')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('非法的token')
        except Exception as e:
            # 所有异常都会走到这
            raise AuthenticationFailed(str(e))
        # 去数据库查出用户
        user = models.New.objects.get(pk=verified_payload.get('user_id'))
        # 认证通过,返回token
        return user, token  # request.user/auth

3.2 settings配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app03.auth2.JWTAuthentication',
    ],
}

3.3 view(login)

from app03.auth import create_token


class Login(APIView):
    # 局部禁用
    authentication_classes = []

    def post(self, request, *args, **kwargs):
        user_obj = models.Topic.objects.get(pk=1)
        token = create_token(user=user_obj)
        return Response({'status': 200, 'token': token})

3.4 接口

from app03.auth2 import JWTAuthentication


class AuctionListView(APIView):
    authentication_classes = [JWTAuthentication, ]

    def get(self, request, *args, **kwargs):
        return Response('ok')

4. jwt介绍

4.1 header

  • 声明类型,这里是jwt

  • 声明加密的算法 通常直接使用 HMAC SHA256

然后将头部进行base64.b64encode()加密(该加密是可以对称解密的),构成了第一部分.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

4.2 payload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息可以存放下面三个部分信息。

  • 标准中注册的声明

  • 公共的声明

  • 私有的声明

标准中注册的声明 (建议但不强制使用) :

  • iss: jwt签发者

  • sub: jwt所面向的用户

  • aud: 接收jwt的一方

  • exp: jwt的过期时间,这个过期时间必须要大于签发时间

  • nbf: 定义在什么时间之前,该jwt都是不可用的.

  • iat: jwt的签发时间

  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

    以上是JWT 规定的7个官方字段,供选用

公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

4.3  signature

JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)

  • payload (base64后的)

  • secret密钥

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

4.4 注意:

secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。  

4.5 jwt的优点:

1. 实现分布式的单点登陆非常方便
2. 数据实际保存在客户端,所以我们可以分担服务器的存储压力
3. JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。

4.6 jwt的缺点:

1. 数据保存在了客户端,我们服务端只认jwt,不识别客户端。
2. jwt可以设置过期时间,但是因为数据保存在了客户端,所以对于过期时间不好调整。#secret_key轻易不要改,一改所有客户端都要重新登录

5. base64转码补充

import base64
str1 = 'admin'
str2 = str1.encode()
b1 = base64.b64encode(str2) #数据越多,加密后的字符串越长
b2 = base64.b64decode(b1) #admin
各个语言中都有base64加密解密的功能,所以我们jwt为了安全,需要配合第三段加密
举报

相关推荐

0 条评论