9、JWT

一、JWT介绍

JWT的全称是Json web token,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密

# JWT就是一段字符串,由三段信息构成的,将这三段信息文本用`.`链接一起就构成了Jwt字符串。就像这样:

# JWT的构成
'''
三段式:
header:一般放公司信息,加密方式 (用处不大)
payload:用户信息: user_id,user_name,email,expire
signature:第一部分和第二部加密得到的
'''

# 通过base64转码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

jwt快速使用

# 使用第三方模块:django-rest-framework-jwt(目前已经停止更新,所以有些公司是不使用)

# 安装:
pip install djangorestframework-jwt


# 快速使用,签发token给前端
	-用户表使用auth的user表
    -登录功能不用写,djangorestframework-jwt帮你写好了

base64的使用

# base64解码
import base64

s = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'
res = base64.b64decode(s)  # 解码
print(res)
''
# base64的编码长度都是4的倍数,如果不足4的倍数,需要用===补齐,最多使用三个等号


# base64编码
import base64
import json

s = {'name': 'poco', 'age': 19}
res = base64.b64encode(json.dumps(s).encode('utf-8'))
print(res)

二、jwt内置认证类使用

1、签发(直接使用内置的)

# 在路由中配置
from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
    path('login/', obtain_jwt_token),  # 内置有登录接口,可以直接签发token,依赖了auth的user表
]

# auth的user表可以用Ctrl+Alt+R,输入createsuperuser命令创建管理员用户

2、认证

from rest_framework_jwt.authentication import JSONWebTokenAuthentication  # drf_jwt内置的认证类
from rest_framework.permissions import IsAuthenticated   # drf内置的权限类

# 想要给哪个视图类加认证就去那个类写
class BookView(APIView):
    # 使用drf_jwt内置的认证,必须加这两个类
    authentication_classes = [JSONWebTokenAuthentication,]  # 认证类
    permission_classes = [IsAuthenticated]  # 权限

url参数携带方式:Authorization=jwt xxxxxxxxxxxxxxxxxx

注意:jwt内置的签发、认证必须依赖auth的user表,如果是自定义的user表,无法使用内置认证类

3、jwt内置签发方法修改返回格式

# 写一个函数(这里我在应用文件夹下创建了utils.py来放置下面函数)
def jwt_response_payload_handler(token, user=None, request=None):  # 参数固定写法、固定位置
    return {  # return什么格式,前端显示的就是什么格式
        'token':token,
        'username':user.username
    }

# 在配置文件中配置
# drf_jwt的配置文件(drf_jwt有个默认配置文件:from rest_framework_jwt import settings)
from rest_framework_jwt.utils import jwt_response_payload_handler  # 导入上面
JWT_AUTH={	
    'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.utils.jwt_response_payload_handler'
}

三、jwt自定义签发认证

1、自定义User表实现jwt的token签发

from rest_framework.response import Response
from .models import UserInfo
from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class LoginView(APIView):
    def post(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = UserInfo.objects.filter(username=username, password=password).first()
        if user:  # 用户名和密码正确,登录成功,签发token
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response({'code': '100', 'msg': '登录成功', 'token': token, 'usrename': user.username})
        else:
            return Response({'code': '101', 'msg': '用户名或密码错误'})

2、自定义认证类,实现jwt的token认证

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
import jwt
from .models import UserInfo
from rest_framework_jwt.settings import api_settings

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


class JwtAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 取出前端传入的token串
        token=request.META.get('HTTP_TOKEN')
        # 通过token获得payload
        try:
            payload = jwt_decode_handler(token) # 通过token串获得payload,验签(是否被篡改),检查过期时间
        # except Exception:
        #     raise exceptions.AuthenticationFailed('token认证失败')
        except jwt.ExpiredSignature:
            msg = '签名过期'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg ='解码错误'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()
        # 通过payload获得当前用户(自己的表,自己拿)
        # user=UserInfo.objects.filter(pk=payload['user_id']).first()
        
        
        # 稍微优化一下,不是每次都取查询当前登录用户
        # user={'id':payload['user_id'],'username':payload['username']}
        user=UserInfo(id=payload['user_id'],username=payload['username'])
        # 返回当前用户
        return user,token

接下来我们就只需要在需要使用认证类的视图类中局部配置(或者去配置文件进行全局配置)

class Index(APIView):
    authentication_classes = [自定义的认证类]  # 局部配置自定义的认证类
    def get(self, request):
        return Response('ok')

然后可以去请求头中带token和token串就行

posted @ 2021-09-20 14:53  黑影Poco  阅读(110)  评论(0)    收藏  举报