DRF

JWT原理介绍

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

JWT的构成

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

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature)。

header:头

jwt的头部承载两部分信息: 声明类型,这里是jwt 声明加密的算法 通常直接使用 HMAC SHA256 完整的头部就像下面这样的JSON:

{
          'typ': 'JWT',
          'alg': 'HS256'
        }

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分。变成了(base64的编码)`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9`

payload:荷载

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

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明 

signature:签名

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

header (base64后的) - payload (base64后的) - secret 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串。

然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。 将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ `

base64的编码与解码 

 base64 可以把字符串编码成base64的编码格式:(大小写字母,数字和 =)

eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogImxxeiIsICJhZG1pbiI6IHRydWV9

可以把base64编码的字符串,解码回原来的格式

应用场景: - jwt中使用 - 网络中传输字符串就可以使用base64编码 - 网络中传输图片,也可能使用base64的编码 

base64编码和解码的过程

编码

复制代码
import json
import base64

d = {'name': 'zxr', 'userid': 1, 'age': 18}
info = json.dumps(d)
# print(info)  # {"name": "zxr", "userid": 1, "age": 18}

# 把字符串转成bytes格式的俩种方式
# 第一种:info.encode('utf-8')
# res = base64.b64encode(info.encode('utf-8'))
# print(res)  # b'eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9'

# 第二种:强转
print(bytes(info, encoding='utf-8'))  # b'{"name": "zxr", "userid": 1, "age": 18}'
复制代码

 

解码

复制代码
import json
import base64

d = {'name': 'zxr', 'userid': 1, 'age': 18}
info = json.dumps(d)
b'eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9'

res = base64.b64decode('eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9')
print(res)  # b'{"name": "zxr", "userid": 1, "age": 18}'
复制代码

 

drf-JWT快速使用

django中使用jwt:

# 可以自己写
https://github.com/jpadilla/django-rest-framework-jwt  (比较老)
https://github.com/jazzband/djangorestframework-simplejwt (比较新)

安装:

pip3 install djangorestframework-jwt

快速使用:

  • 迁移表,因为它默认使用auth的user表签发token
  • 创建超级用户(auth的user表中要右记录)
  • 不需要写登录接口了,如果是使用auth的user表作为用户表,它可以快速签发
  • 签发(登录):只需要在路由中配置(因为它帮咱们写好登录接口了)
复制代码
from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
    path('login/', obtain_jwt_token),
]

# 返回结果
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inp4ciIsImV4cCI6MTY2NTU3MjU4OSwiZW1haWwiOiJ6eHJAMTYzLmNvbSJ9.0jDd56Jk04-SdZ4AchLHkcfLECS1RvhFwAQ8VoNKiMM"
}
复制代码
  • 认证(认证类):导入,配置在视图类上
复制代码
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication


class TestView(APIView):
    authentication_classes = [JSONWebTokenAuthentication, ]
    # 是否登录的权限类
    permission_classes = [IsAuthenticated, ]
    def get(self, request):
        return Response('ok')
复制代码
  • 前端访问时,token需要放在请求头中
Authorization:jwt token串

# 例如:
    Authorization:jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inp4ciIsImV4cCI6MTY2NTU3MzMzMCwiZW1haWwiOiJ6eHJAMTYzLmNvbSJ9.DAX1wLhHYnpGwVzGvMnSFzUudkPgXsYvlrfk-XFdSAc

drf-jwt修改返回格式

登录成功后,前端看到的格式,太固定了,只有token,我们想做成:

{code:100,msg:'登录成功',token:adfasdfasdf}

固定写法:

写一个函数,函数返回什么,前端就看到什么,配置在配置文件中

使用步骤:

写一个函数:

def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'code': 100,
        'msg': '登录成功',
        'username': user.username,
        'token': token
    }

将函数配置在配置文件中:

# djangorestframework-jwt的配置,这个配置了以后优先使用这个
JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_response_payload_handler',
}

 

自定义user表,签发token:

复制代码
# view.py
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from app01.models import Userinfo
from rest_framework.exceptions import APIException

from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
# 自己基于自定义的用户表签发token
class UserView(APIView):
    def post(self, request):
        try:
            username = request.data.get('username')
            password = request.data.get('password')
            user = Userinfo.objects.get(username=username, password=password)
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        except Exception as e:
            raise APIException('用户表名或密码错误')
复制代码
复制代码
# url.py
from django.contrib import admin
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token

from app01 import views

urlpatterns = [
    path('user/login/', views.UserView.as_view()),
]
复制代码
# models.py
from django.db import models

# Create your models here.
class Userinfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

自定义认证类,验证token:

# view.py
class UserTestView(APIView):
    authentication_classes = [JWTAuthentication, ]

    def get(self, request):
        return Response('ok')
复制代码
# 自定义认证类
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.settings import api_settings
from rest_framework.exceptions import AuthenticationFailed
import jwt
from .models import Userinfo

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_TOKEN')
        print(token)
        jwt_value = request.META.get('HTTP\_TOKEN')
        if jwt_value:
            try:
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                msg = 'Signature has expired.'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.DecodeError:
                msg = 'Error decoding signature.'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.InvalidTokenError:
                raise exceptions.AuthenticationFailed()
            user = payload.get('user_id')
            return user, jwt_value
        else:
            raise AuthenticationFailed('您没有携带token')
复制代码

 

posted @   *sunflower*  阅读(97)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示