drf-jwt

1. cookie,session、token介绍

cookie介绍

cookie指的时浏览器里面能够永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能

cookie由服务器生成,发送给浏览器。

浏览器把cookie以KV键值对的形式保存到某个目录下的文本文件内,下次请求同一网站时会把cookie发送给服务器。

由于cookie时存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多的磁盘空间,所以每个域的cookie数量时有限的

session介绍

session从字面上讲,就是会话,也叫会话保持机制

当客服端向服务端访问,服务器要知道当前发送请求给自己的是谁,为了做这种区分,服务器就要给每个客户端分配不同的身份标识。

然后客户端每次向服务器发送请求的时候,都要带上这个身份标识,服务器就知道这个请求来自于谁了

至于客户端怎么保存这个身份标识可以有很多种方式,对于浏览器客户端,大部分都默认采用cookie的方式。

服务器使用session把用户的信息临时保存在服务器上,yoghurt离开网站后session会被销毁,这种用户信息存储方式相对于cookie来说更安全。

可是sesion有一个缺陷,如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。

cookie与session的区别

session是存放在服务端,cookie是存放在客户端,所以session的安全性比cookie高。

获取session里的信息是通过存放在会话cookie里的session id获取的,

而session是存放在服务器的内存中,所以session里的数据不断增加会造成服务器的负担,所以会把很重要的信息存放在session中,而客户端的cookie里只存放一些次要的东西。

session的信息是通过sessionid获取的,而sessionid是存放在会话cookie中

当浏览器关闭的时候会话cookie消失,所以sessionid也就消失了,但是session的信息还存在服务器端,只是查不到所谓的session,但它并不是不存在

token

在Web领域基于Token的身份验证随处可见,在大多数使用Web API 的互联网公司中,tokens是多用户下处理认证的最佳方式。

基于Token的身份验证是无状态的,我们不用将用户信息存放在服务器或session中,这种概念解决了在服务端存储信息时的许多问题。

没有session信息意味着你的程序可以根据需要去增减机器,而不用去担心用户是否登录和已经登录到了哪里

  • 虽然基于Token的身份验证实现的方式很多,但大致过程如下:

    1. 用户通过用户名和密码发送请求
    2. 程序验证
    3. 程序返回一个签名的token给客户端
    4. 客户端存放token,并且以后每次请求都会附带它
    5. 服务端验证token,并且返回数据
  • token:三段式

    第一段:头 __ 公司信息,加密方式

    第二段:荷载__真正的数据

    第三段:签名__通过第一段和第二段,通过某种加密方式得到的

  • token的使用分为俩个阶段

    登录成功后的,签发token阶段,生成三段

    img

    登录成功访问某个接口的,验证阶段,验证token是否合法

    img

2. jwt原理介绍

Jwt 全称(Json web token),token的应用于web方向的称之为jwt

构成和工作原理:

JWT就是一段字符串,由三段信息构成的,将这三段信息文本用点号链接一起就构成了JWT字符串

如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
  • header:头(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9)

    -- 声明类型,这里是jwt

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

    -- 公司信息...

    # 由
    	{
       		'typ': 'JWT',
       		'alg': 'HS256'
    	 }
    
    使用base64的编码变成了:
    	eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    
  • payload:荷载

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

    iat:jwt的签发时间

    用户信息:用户信息

    # 由
    	 {
              "exp": "1234567890",
              "name": "John Doe",
              "userid": 3
            }
    使用base64的编码变成了:
    	eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
    
  • signature:签名

    把头和荷载加密加盐后得到签名:

    TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    
  • 注意

    secret是保存在服务器端的(加密方式+盐),jwt的签发生成也是在服务器端的。

    secret就是用来进行jwt的签发和jwt的验证,它就是你服务端的私钥。

    在任何场景都不应该流露出去,一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了

    jwt使用流程最核心的就是:签发(登录接口的签发),认证(认证类的认证)

  • 基于session的认证机制概

    • 登录阶段--- 校验用户是否登录成功

      --成功,在经过中间件的session时会在process_response内执行

      1.生成一个随机字符串

      2.在把django-sesion中存入

      3.把随机字符串为cokie,返回给浏览器

      --不成功,直接返回。

    • 登录成功后,访问阶段

      登录了去访问,携带session id = 随机字符串

      经过django中间件,在process_request中

      1. 取出携带的随机字符串
      2. 去django-session表中取出数据
      3. 转成session对象(字典)
      4. request.session=session
      

      在视图函数中,就可以使用 request.session

  • 基于jwt的认证概括

    • 登录阶段

      1. 校验用户是否正确

      2. 错误直接返回

      3. 成功则签发token

      1. 头是固定的,荷载(不同用户不一样,用户名和id)
      2. 通过头和荷载,加密+加盐处理(密钥)==签名
      3. 头.荷载.签名
      1. 返回给客户端
      2. 客户端将token存放起来
    • 登录过后访问某个接口:携带token

      认证类中:

      1. 取出携带的token

      2. 校验token

      1. 取出头,取出荷载
      2. 使用同样的加密+加盐处理对头和荷载签名得到新的签名
      3. 把新签名,与老签名(token的第三部分)校验
      4. 一样说明没有被篡改从荷载内取出用户id,去数据查出当前登录用户
      1. 校验通过,后续的request.user就是当前用户

      2. 检验不通过,说明token不对直接返回不通过,需要重新登录

3. base64编码与解码

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

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

  • 应用场景

    --jwt中使用

    --网络中传输字符串就可以使用base64编码

    --网络中传输图片,也可以使用base64编码

  • 实际操作

    import json
    from base64 import b64encode, b64decode
    
    # 编码,
    s = {'code': 100, 'format': 'base64编码'}
    s = json.dumps(s)
    res = b64encode(s.encode('utf8'))
    print(res)  # 编码结果  b'eyJjb2RlIjogMTAwLCAiZm9ybWF0IjogImJhc2U2NFx1N2YxNlx1NzgwMSJ9'
    
    # 解码
    	res1 = b64decode(res)
    	print(res1)  # 解码结果  b'{"code": 100, "format": "base64\\u7f16\\u7801"}'
    	res1 = json.loads(res1)
    	print(res1)   # {'code': 100, 'format': 'base64编码'}
    	print(res1.get('code'))  # 100
    
    

4. drf-jwt快速使用

https://github.com/jpadilla/django-rest-framework-jwt (比较老)

https://github.com/jazzband/djangorestframework-simplejwt (比较新)

  • 安装

    pip3 install djangorestframework-jwt

  • jwt的快速使用

    迁移表,因为它默认使用auth的user表签发token

    创建超级用户(auth的user表中要有记录)

    如果是使用auth的user表作为用户表,它可以快速签发,也不需要写登录接口(它已经帮我们写好了)

    签发(登录):只需要在路由中配置

    from rest_framework_jwt.views import obtain_jwt_token
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', obtain_jwt_token),
    ]
    

    postmant测试

    添加认证(认证类):导入,配置在视图类上

    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
    
    
    class UserView(APIView):
        # 使用jwt提供的认证类需要配合一个权限类一起才能使用
        authentication_classes = [JSONWebTokenAuthentication,]
        permission_classes = [IsAuthenticated,]
        def get(self, request):
            return Response('ok')
    

    前端访问时,token需要放在请求头中

    key值名称:Authorization value值:jwt token串

5. drf-jwt修改返回格式

# 登录成功后,前端看到的格式,太固定了,中有token需要自己写一个函数自定义返回格式

# 函数返回什么前端就看到什么,配置在配置文件中
# 写函数一个函数
def jwt_handler(token,user=)
	return {
        'code':100,
        'msg':'登录成功',
        'username':user.username,
        'token':token
    }

# 把函数配置在配置文件中
	JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_handler'
}

# 这样以后登录接口返回的格式就是我们写的函数的返回值

6. 自定义user表,签发token


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

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

练习

自定义认证类验证token

from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.settings import api_settings
from rest_framework import exceptions

from .models import UserInfo

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER

class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_TOKEN')
        try:
            payload = jwt_decode_handler(token)
        except Exception as e:
            raise exceptions.AuthenticationFailed("token错误")

        user_obj = UserInfo.objects.filter(pk=payload.get('user_id'))
        return user_obj, token
posted @   瓮小辉  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示