DRF-(9)

内容概览

  • cookie,session,token介绍
  • jwt原理介绍
  • base64编码与解码
  • drf-jwt快速使用
  • drf-jwt修改返回格式
  • 自定义user表,签发token

cookie,session,token介绍

  • 无cookie
    在很早之前,web基本就是文档的浏览,不需要记录用户,每次请求都是一个新的http请求
  • cookie+session
    随着交互式web应用的兴起,像在线购物网站,需要登录的网站等等,马上就面临一个问题,那就是要管理会话,必须记住哪些人登录系统, 哪些人往自己的购物车中放商品, 也就是说我必须把每个人区分开,这就是一个不小的挑战,因为HTTP请求是无状态的,所以想出的办法就是给大家发一个会话标识(session id), 说白了就是一个随机的字串,每个人收到的都不一样, 每次大家向我发起HTTP请求的时候,把这个字符串给一并捎过来, 这样我就能区分开谁是谁了
  • cookie+session存在问题
    每个人只需要保存自己的session id,而服务器要保存所有人的session id ! 如果访问服务器多了, 就得由成千上万,甚至几十万个。
    这对服务器说是一个巨大的开销 , 严重的限制了服务器扩展能力
  • Token认证
    token认证分为三段
    第一段:头,公司信息,加密方式…
    第二段:荷载,真正的数据信息
    第三段:签名,通过第一段和第二段,通过某种加密方式加密得到
    用户登录后将第一段加第二段在加上自己才知道的盐,通过某种加密方式得到第三段签名,将整个返回给客户端保存
    用户再次访问时携带这个组数据,服务端再获取第一段与第二段以原来的方式加密,与第三段签名对比,相同则数据正常
    使用token的认证机制后,服务端就不需要保存数据,而是保存在各自的客户端上了

jwt原理介绍

JWT(Json Web Token),token的应用于web方向的就称为jwt

# 构成和工作原理
	JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
# header:头
	1. 声明类型,这里是jwt
	2. 声明加密的算法 通常直接使用 HMAC SHA256
	3. 公司信息
	由{'typ': 'JWT','alg': 'HS256'}
	变成了(base64的编码):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
# payload:荷载
	1. exp: jwt的过期时间,这个过期时间必须要大于签发时间
	2. iat: jwt的签发时间
	3. 用户信息: 用户信息
	由{"exp": "1234567890","name": "John Doe","userid": 3}
	变成了(base64的编码):eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
# signature:签名
	把头和荷载加密后得到的:TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
        
        
"""注意:secret是保存在服务器端的(加密方式+盐),jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了"""
    
# jwt使用流程最核心的是:
	签发:登录接口签发
	认证:认证类认证

base64编码与解码

base64可以把字符串编码成base64的编码格式
与加密不同,base64可以解码回原来的数据
应用场景:jwt中使用;网络中传输字符串可以使用;网络中传输图片可以使用

# 使用方式
import base64
import json

d1 = {'name':'al', 'age': 17}
d1 = json.dumps(d1)
res = base64.b64encode(d1.encode())  # 只能够编码bytes类型字符串,所以先序列化为json格式字符串,转为bytes格式再编码
print(res)  # b'eyJuYW1lIjogImFsIiwgImFnZSI6IDE3fQ=='

bs = 'eyJuYW1lIjogImFsIiwgImFnZSI6IDE3fQ=='
bs = base64.b64decode(bs)  # 可以直接解码字符串和bytes类型的数据
print(bs)  # b'{"name": "al", "age": 17}'

drf-jwt快速使用

django中使用jwt有三种方式

  1. 自己写
  2. 使用djangorestframework-simplejwt(比较新)
  3. 使用django-rest-framework-jwt(比较老)
1. 安装第三方模块
pip install djangorestframework-jwt
2. 迁移表,因为它默认使用auth的user表签发token
python manage.py makemigrations
python manage.py migrate
3. 创建超级用户(auth的user表中要有记录)
4. 不需要写登录接口,如果使用的auth的user表作为用户表,他可以快速签发
5. 直接配置路由,因为它已经写好了登录接口
from rest_framework_jwt.views import obtain_jwt_token
	
urlpatterns = [
    path('login/', obtain_jwt_token),
]
6. 在视图类上配置认证类,还需要配合权限类使用
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

class TestView(APIView):
    authentication_classes = [JSONWebTokenAuthentication]
    permission_classes = [IsAuthenticated]
    def get(self, reqeust):
        return Response('ok')
7. 前端访问时,token需要放在请求头中
Authorization:jwt token串

drf-jwt修改返回格式

"""登录成功后,前端看到的格式是固定的,如果想要修改返回的格式,需要写一个函数,配置到配置文件中,函数返回什么前端就看到什么"""
# 1. 先写一个函数
def jwt_response(token, user=None, request=None):
    return {'code': 100, 'msg': '登录成功', 'username': user.username, 'token': token}
# 2. 将函数配置到配置文件中
JWT_AUTH={
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.views.jwt_response'
}
"""配置完成后,以后登录接口返回的格式就是我们写的函数的返回值"""

自定义user表,签发token

# 1. 先创建表模型(models.py)
from django.db import models

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    
# 2. 写一个登录接口(views.py)
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

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:
        	"""使用djagnorestframework-jwt模块提供的签发token的函数,生成token"""
            payload = jwt_payload_handler(user) # 通过user对象---》{username:lqz,id:1,过期时间}
            token = jwt_encode_handler(payload) # 根据payload---》得到token:头.荷载.签名
            return Response({'code':100,'msg':'登录成功','token':token})
        raise APIException('用户名或密码错误')

练习

自定义认证类,验证token

"""认证类"""
import jwt

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.utils import jwt_decode_handler
from app01.models import UserInfo


class Jwt_Auth(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_JWT')
        if token is None:
            return None
        try:
            payload = jwt_decode_handler(token)
        except jwt.ExpiredSignature:
            msg = '签名已经过期'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = '错误解码签名。'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()
        user = UserInfo.objects.get(pk=payload.get('user_id'))
        return (user, token)

"""权限类"""
from rest_framework.permissions import BasePermission


class Jwt_permission(BasePermission):

    def has_permission(self, request, view):
        return bool(request.user)

"""视图类"""
from app01.models import UserInfo
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from rest_framework_jwt.settings import api_settings
from app01.authentications import Jwt_Auth
from app01.permissions import Jwt_permission

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER



class TestView(APIView):
    authentication_classes = [Jwt_Auth]
    permission_classes = [Jwt_permission]

    def get(self, reqeust):
        return Response('ok')


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:
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        raise APIException('用户名或密码错误')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值