drf学习笔记

今日内容概要

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

今日内容详细

cookie,session,token介绍

cookie是:存在客户端浏览器的键值对
session是:存在于服务端的键值对
两者区别:
session是存储服务器端,cookie是存储在客户端,所以session的安全性比cookie高
session的信息是通过sessionid获取的,而sessionid是存放在会话cookie中

token是:三段式,服务端生成的,存放在客户端(浏览器就放在cookie中,移动端:存在移动端中)
token:三段式
第一段:头:公司信息,加密方式...以字典形式{}
第二段:荷载:真正的数据{'name':'lqz', 'id':1}
第三段:签名:通过第一段和第二段,通过某种加密方式加密得到的

token的使用分两个阶段:
1.登陆成功后的【签发token阶段】,它生成了三段
2.登录成功访问某个接口的【验证阶段】,它验证token是否合法

注意:使用token的认证机制,服务端还要存数据吗?
token是服务端生成的,客户端保存,服务端不存储token。


详情链接:https://www.cnblogs.com/liuqingzheng/p/8990027.html

签发token阶段:

img

验证阶段:

img

jwt原理介绍

jwt:Json web token (JWT), token的应用于web方向的称之为jwt。

jwt的构成

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

第一段:头(header)

jwt的头部承载两部分信息:
声明类型:这里是jwt
声明加密的算法:通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:
{
  'typ': 'JWT',
  'alg': 'HS256'
}
然后将头部进行base64编码,构成了第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

第二段:荷载(payload)

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
exp: jwt的过期时间,这个过期时间必须要大于签发时间
 iat: jwt的签发时间
用户信息: 用户信息
一般可以用来存放用户信息:
{
    "exp": "1234567890",
    "name": "John Doe",
    "userid": 3
}
然后将其进行base64编码,得到JWT的第二部分:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

第三段:签名(signature)

JWT的第三部分是一个签证信息,这个签证信息是把头和荷载通过某种加密方式+秘钥加密得到一个字符串:
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

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

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

jwt使用流程最核心的就是:

签发:登录接口签发
1)用账号密码访问登录接口,登录接口逻辑中调用签发token算法,得到token,返回给客户端,客户端自己存到cookies中

认证:认证类认证
2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用

基于session和jwt的认证原理:

image

image

签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token

1)用基本信息存储json字典,采用base64算法加密得到 头字符串
2)用关键信息存储json字典,采用base64算法加密得到 体字符串
3)用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串

账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台

校验:根据客户端带token的请求 反解出 user 对象

1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户

base64编码和解码

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

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

应用场景:
1. jwt中使用
2. 网络中传输字符串
3. 网络中传输图片(不常用)

import json
import base64

d = {'name': 'lqz', 'userid': 6, 'age': 19}
info = json.dumps(d)
print(info)

# 把字符串使用base64编码
res = base64.b64encode(info.encode('utf-8'))
print(res) # eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9

# 把字符串使用base64解码
d = 'eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9'
res = base64.b64decode(d)
print(res) # b'{"name": "lqz", "userid": 6, "age": 19}'

# 把图片保存起来看看
s = '图片的base64解码数据'
res=base64.b64decode(s)
with open('code.png','wb') as f:
    f.write(res)

drf-jwt快速使用

可以帮我们快速实现 jwt: 签发(登录接口) 认证(认证类)

django中使用jwt:
1. 可以自己写
2. 使用模块djangorestframework-jwt:https://github.com/jpadilla/django-rest-framework-jwt  (比较老)
3. 使用模块djangorestframework-simplejwt:https://github.com/jazzband/djangorestframework-simplejwt (比较新)

# 安装
    pip3 install djangorestframework-jwt
# 快速使用
    1. 迁移表,因为它默认使用auth的uesr表签发token
    2. 创建超级用户(auth的user表中要有记录)
    3. 签发(登录):只需要在路由中配置(因为它帮咱们写好登录接口了)
        from rest_framework_jwt.views import obtain_jwt_token
        urlpatterns = [
            path('login/', obtain_jwt_token),
        ]
    4. 认证(认证类):导入,配置在视图类上
        class TestView(APIView):
            authentication_classes = [JSONWebTokenAuthentication,]
            permission_classes = [IsAuthenticated,]

    5. 前端访问时,token需要放在请求头中
        Authorization:jwt token串 (jwt空格token串)

drf-jwt修改返回格式

登录成功后,前端看到的格式,太固定了,只有token。

要求登录成功格式:

{
    'code': 100,
    'msg': '登录成功',
    'token': 随机字符串
}

使用步骤:

1.写一个函数

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

2.把函数配置在配置文件中

JWT_AUTH={
        'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_response_payload_handler',
}

自定义user表,签发token

自己定义用户表,实现签发token功能,自己写登录接口。

# models.py 中定义UserInfo表

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

# 写一个登录接口
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

from .models import UserInfo


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)
            # 根据user,签发token---》三部分:头,荷载,签名
            # 使用djagnorestframework-jwt模块提供的签发token的函数,生成token
            payload = jwt_payload_handler(user) # 通过user对象---》{username:lqz,id:1,过期时间}
            token=jwt_encode_handler(payload) # 根据payload---》得到token:头.荷载.签名
            print(payload)
            print(token)

            return Response({'code':100,'msg':'登录成功','token':token})
        except Exception as e:
            raise APIException('用户名或密码错误')

作业

# views.py
from django.shortcuts import render

# Create your views here.

from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .models import User
from rest_framework.exceptions import APIException
from .auth import LoginAuth


class TestView(APIView):
    #authentication_classes = [JSONWebTokenAuthentication, ]
    #permission_classes = [IsAuthenticated, ]
    authentication_classes = [LoginAuth, ]

    def post(self, request):
        return Response('ok')


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 UserView(APIView):

    def post(self, request):
        try:
            username = request.data.get('username')
            password = request.data.get('password')
            user = User.objects.get(username=username, password=password)

            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            print(token)
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        except Exception as e:
            raise APIException('用户名或密码错误')

# auth.py
from rest_framework_jwt.utils import jwt_response_payload_handler
from rest_framework.authentication import BaseAuthentication
import jwt
from rest_framework import exceptions


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


from rest_framework_jwt.settings import api_settings

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
from .models import User


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_TOKEN')
        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:
            msg = '解析签名未知错误'
            raise exceptions.AuthenticationFailed(msg)

        user = User.objects.get(pk=payload['user_id'])
        return user, token

# settings.py
JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.auth.jwt_response_payload_handler',
}

# 请求头中:X_FORWARDED_FOR 代之什么?
X-Forwarded-For请求头格式非常简单,就这样:
X-Forwarded-For:client, proxy1, proxy2

可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,
最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。 

如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,
IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:
X-Forwarded-For: IP0, IP1, IP2

Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求。
列表中并没有 IP3,IP3 可以在服务端通过 remote_address 字段获得。
remote_address 来自 TCP 连接,表示与服务端建立 TCP 连接的设备 IP,在这个例子里就是 IP3。

对程序来说,读取的 request.remote_addr 也永远是直接跟他链接的 ip。

# 集群 ,分布式
集群:

集群是指在几个服务器上部署相同的应用程序来分担客户端的请求。

它是同一个系统部署在不同的服务器上。

集群主要的使用场景是为了分担请求的压力,但是,当压力增大的时候,可能需要存储的部分,比如mysql无法面对大量的写操作,因为MySQL做成集群后,主要的写压力还是在master(主机)的机器上,其他slave(从机)机器无法分担写压力,这时就引出了“分布式”!

分布式

分布式是指多个系统协同合作完成一个特定任务的系统。

它是不同的系统部署在不同的服务器上,然后服务器之间相互调用。

分布式解决了中心化管理的问题。把所有的任务叠加到一个节点处理,效率太慢,

所以把一个大的问题拆分成多个小问题,并分别解决,最终协同合作,。

分布式主要工作:分解任务,把任务拆解。

最后,再深入理解一下集群和分布式及其区别:

分布式:把一个大业务拆分成多个子业务,每个子业务都是一套独立的系统,子业务之间相互协作最终完成整体的大业务。

集群:把处理同一个业务的系统部署多个节点 。

把一套系统拆分成不同的子系统部署在不同服务器上,这叫分布式。

把多个相同的系统部署在不同的服务器上,这叫集群。部署在不同服务器上的相同系统必然要做“负载均衡”。

集群主要是简单加机器解决问题,对于问题本身不做任何分解。

分布式处理里必然涉及任务分解与答案归并。分布式中的某个子任务节点,可以是一个集群,该集群中的任一节点都作为一个完整的任务出现。

集群和分布式都是由多个节点组成,但集群中各节点间基本不需要通信协调,而分布式中各个节点的通信协调是必不可少的。
posted @   空白o  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示