当前位置:   article > 正文

Django-rest-framework框架之JWT认证_djangorestframework-jwt

djangorestframework-jwt

今日内容概要

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

coookie,session,token介绍

参考博文地址:https://www.cnblogs.com/liuqingzheng/p/8990027.html
1.coookie,session,token发展史

  • 无cookie时期
    有一段时间Eeb基本上就是文档的浏览时期,既然是浏览,作为服务器,不需要记录某人在某段时间都浏览了什么文档,每次的请求都是一个新的HTTP协议,就是请求加响应,不需要记住是谁发了HTTP请求,每个请求对于“我”来说都是全新的。
  • cookie+session认证时期
    随着交互式Web应用的兴起,一些在线购物网站,需要登录的网站即需要面临一个管理会话的问题。这些网站必须要记住哪些人登录系统,哪些人购买商品或者各种功能实现,也就是说我们必须把每个人与他在该网站所做的行为区分开,这形成了新的挑战,因为HTTP请求是无状态的。
    所以想出的办法就是给大家发一个会话标识(session id),即发送一个随机字符串,每个人的随机字符串不同,每次该网站收到发送来的HTTP请求时,连同该字符串一起发送过来,这样就对每个人起到区分的作用。
  • cookie+session存在问题即解决
    接下来面临的新问题是服务器很难过,表示本宝宝不服,每个人只需要保存自己的(session id),可本宝宝要保存你们所有人的session id,这可不是一个小数目。
    所以,这对服务器来说变成了一个巨大的开销,严重的限制了服务器扩展能力。比如说我用两个机器组成了一个集群,小F通过通过机器A登录了系统,那session id 会保存在机器A上,那假设小F的下一次请求被转到机器B上怎么办?机器B上并没有存小F的session id。
    有时候会采用一点小伎俩: session sticky , 就是让小F的请求一直粘连在机器A上, 但是这也不管用, 要是机器A挂掉了, 还得转到机器B去。
    那只好做session 的复制了, 把session id 在两个机器之间搬来搬去, 快累死了。
    在这里插入图片描述
    后来有个叫Memcached的支了招: 把session id 集中存储到一个地方, 所有的机器都来访问这个地方的数据, 这样一来,就不用复制了, 但是增加了单点失败的可能性, 要是那个负责session 的机器挂了, 所有人都得重新登录一遍, 估计得被人骂死。
    在这里插入图片描述
    也尝试把这个单点的机器也搞出集群,增加可靠性, 但不管如何, 这小小的session 对我来说是一个沉重的负担。
  • Token认证
    于是人们开始思考让客户端去保存session。
    可是服务端不保存这些session id的话,怎么验证客户端发给我的session id的却是我生成的呢? 如果不去验证,我们都不知道他们是不是合法登录的用户, 那些不怀好意的家伙们就可以伪造session id , 为所欲为了。
    这里即提出关键字为验证!!!
    比如说, 小F已经登录了系统, 我给他发一个令牌(token), 里边包含了小F的 user id, 下一次小F 再次通过Http 请求访问我的时候, 把这个token 通过Http header 带过来不就可以了。
    不过这和session id没有本质区别,于是提出对数据做一个签名,比如说我用HMAC-SHA256 算法,加上一个只有我才知道的密钥, 对数据做一个签名, 把这个签名和数据一起作为token , 由于密钥别人不知道, 就无法伪造token了。
    在这里插入图片描述
    这个token 我不保存, 当小F把这个token 给我发过来的时候,我再用同样的HMAC-SHA256 算法和同样的密钥,对数据再计算一次签名, 和token 中的签名做个比较, 如果相同, 我就知道小F已经登录过了,并且可以直接取到小F的user id , 如果不相同, 数据部分肯定被人篡改过, 我就告诉发送者: 对不起,没有认证。
    在这里插入图片描述
    Token 中的数据是明文保存的(虽然我会用Base64做下编码, 但那不是加密), 还是可以被别人看到的, 所以我不能在其中保存像密码这样的敏感信息。
    当然,如果一个人token被别人偷走并发送给我,我也自能默认其为合法用。
    故我们就不需要存session id,只要生成token即可。

2.Cookie,Session解释

  • Cookie
    cookie 是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。
    cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。

  • Session
    session 从字面上讲,就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。
    session 也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。
    服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。

  • cookie和session的区别
    session是存储服务器端,cookie是存储在客户端,所以session的安全性比cookie高
    获取session里的信息是通过存放在会话cookie里的session id获取的。而session是存放在服务器的内存中里,所以session里的数据不断增加会造成服务器的负担,所以会把很重要的信息存储在session中,而把一些次要东西存储在客户端的cookie里。
    session的信息是通过sessionid获取的,而sessionid是存放在会话cookie中
    当浏览器关闭的时候会话cookie消失,所以sessionid也就消失了,但是session的信息还存在服务器端,只是查不到所谓的session,但它并不是不存在

3.token解释

  • token三段式:
    第一段:头:公司信息,加密方式等{}
    第二段:荷载:真正的数据{name:jason,id:1}
    第三段:签名:通过第一段和第二段,通过某种加密方式加密得到的
  • token的使用分两个阶段
    登录成功后【签发token阶段】------》此阶段生成token三段式;
    登录成功访问某个接口的【验证阶段】------》验证token是否合法
  • 三者区别
    cookie是存在客户端浏览器的键值对
    session是存在于服务端的键值对
    token是三段式,服务端生成的,存放在客户端(浏览器就放在cookie中,移动端:存在移动端中)

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

jwt原理介绍

1.概念
Json web token (JWT), token的应用于web方向的称之为jwt
核心:
签发:登录接口签发;
认证:认证类认证

2.构成和工作原理
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
我们把它拆分一下:

  • header:头
    (1)声明类型,这里是jwt
    (2)声明加密的算法:通常直接使用HMAC SHA256
    (3)举个栗子:公司信息
    由{
    ‘type’:‘JWT’,
    ‘alg’:‘HS256’
    }
    变成了(base64的编码)
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  • payload:荷载
    (1)exp:jwt的过期时间,这个时期时间必须要大于签发时间
    (2)lat:jwt的签发时间
    (3)举个栗子:用户信息
    由{
    “exp”: “1234567890”,
    “name”: “John Doe”,
    “userid”: 3
    }
    变成了(base64的编码)
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
  • signature:签名
    把头和荷载加密后得到的:
    TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

3/基于session的认证机制
请添加图片描述
4.基于jwt的认证
请添加图片描述

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

base64编码和解码

1.概念

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

2.应用场景

  • jwt中使用
  • 网络中传输字符串就可以使用base64编码
  • 网络中传输图片,也可能使用base64的编码

3.代码操作
需导入内置base64模块

import base64
import json
d = {'name':'lqz','userid':6,'age':19}
info = json.dumps(d)
print(info)    # {"name": "lqz", "userid": 6, "age": 19}
# 把字符串使用base64编码
res = base64.b64encode(info.encode('utf-8'))
print(res)     # >eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9


# 使用base64解码过程
res = base64.b64decode('eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9')
print(res)    # {"name": "lqz", "userid": 6, "age": 19}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
'
运行

drf-jwt快速使用

1.jwt
签发:登录接口
认证:认证类

2.django中使用jwt

  • 可以自己写
https://github.com/jpadilla/django-rest-framework-jwt  (比较老)

https://github.com/jazzband/djangorestframework-simplejwt (比较新)
  • 1
  • 2
  • 3
  • python第三方模块(djangorestframework-jwt)
    (1)安装:pip3 install djangorestframework-jwt
    (2)快速使用:
    迁移表:因为它,默认使用auth的user表签发token;
    创建超级用户:在auth的user表中要有记录;
    不需要在写接口,如果是使用auth的user表作为用户表,它可以实现快速签发;
    签发(登录):只需要在路由中配置(因为该第三方模块已经帮我们写好登录接口了)
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('admin/', admin.site.urls),
   path('login/',obtain_jwt_token),
   path('text/',views.TestView.as_view()),
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

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 TestView(APIView):
   # 登录后才能访问:加一个认证类
   # 使用jwt提供的认证类,必须配合权限类
   authentication_classes = [JSONWebTokenAuthentication, ]
   permission_classes = [IsAuthenticated, ]

   def get(self, request):
       return Response('ok')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

前端访问时:token需要放在请求头中
Authorization:jwt token串
请添加图片描述

drf-jwt修改返回格式

1.提出需求
我们要在登录成功以后,前端看到的格式不再单一的只有token,而是做成{code:100,msg:‘登录成功’,token:adfasdfasdf}

2.解决措施
固定写法:写一个函数,函数返回什么,前端就看到什么,配置在配置文件中

3.操作步骤

  • 写一个函数(prompt.py文件内写)
from rest_framework_jwt.utils import jwt_response_payload_handler

def jwt_response_payload_handler(token, >user=None, request=None):
   return {
       'code': 100,
       'msg': '登录成功',
       'username': user.username,
       'token': token
   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 把函数配置在配置文件中
# djagnorestframework-jwt的配置
JWT_AUTH={
'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.prompt.jwt_response_payload_handler',
}
  • 1
  • 2
  • 3
  • 4
'
运行
  • 以后登录接口返回的格式就是咱们写的函数的返回值

自定义user表,签发token

models.py中定义UserInfo表:

class UserInfo(models.Model):
   username = models.CharField(max_length=32)
   password = models.CharField(max_length=32)
  • 1
  • 2
  • 3

写一个登录接口:

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('用户名或密码错误')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号