【Django-rest-framework框架】 第10回 cookie,session,token介绍、base64编码和解码、jwt使用

1. Cookie,Session,Token介绍

1.1.1 什么是Cookie

Cookie翻译成中文的意思是‘小甜饼’,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。

服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。

Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。Cookie存储的数据量有限,且都是保存在客户端浏览器中。不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用Cookie实际上只能存储一小段的文本信息(key-value格式)。

1.1.2 Cookie的机制

当用户第一次访问并登录一个网站的时候,cookie的设置以及发送会经历以下4个步骤:

客户端发送一个请求到服务器;

服务器发送一个HttpResponse响应到客户端,其中包含Set-Cookie的头部;

客户端保存cookie,之后向服务器发送请求时,HttpRequest请求中会包含一个Cookie的头部;

服务器返回响应数据。

1.1.3 Cookie的属性

Expires
该属性用来设置Cookie的有效期。Cookie中的maxAge用来表示该属性,单位为秒。Cookie中通过getMaxAge()和setMaxAge(int maxAge)来读写该属性。maxAge有3种值,分别为正数,负数和0。

如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中(每个浏览器存储的位置不一致)。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。

Cookie cookie = new Cookie("jiangwang",System.currentTimeMillis()+"");

// 设置生命周期为MAX_VALUE,永久有效

cookie.setMaxAge(Integer.MAX_VALUE);resp.addCookie(cookie);

当maxAge属性为负数,则表示该Cookie只是一个临时Cookie,不会被持久化,仅在本浏览器窗口或者本窗口打开的子窗口中有效,关闭浏览器后该Cookie立即失效。

修改或者删除Cookie
HttpServletResponse提供的Cookie操作只有一个addCookie(Cookie cookie),所以想要修改Cookie只能使用一个同名的Cookie来覆盖原先的Cookie。如果要删除某个Cookie,则只需要新建一个同名的Cookie,并将maxAge设置为0,并覆盖原来的Cookie即可。

新建的Cookie,除了value、maxAge之外的属性,比如name、path、domain都必须与原来的一致才能达到修改或者删除的效果。否则,浏览器将视为两个不同的Cookie不予覆盖。

1.1.4 Cookie的域名和路径

Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie。

正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,比如a1.jiangwang.com和a2.jiangwang.com,因为二者的域名不完全相同。如果想要jiangwnag.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为.jiangwang.com,这样使用a1.jiangwang.com和a2.jiangwang.com就能访问同一个cookie

path属性决定允许访问Cookie的路径。比如,设置为"/"表示允许所有路径都可以使用Cookie

1.1.5 应用

Cookies最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是Cookies的功用。另一个重要应用场合是“购物车”之类处理。用户可能会在一段时间内在同一家网站的不同页面中选择不同的商品,这些信息都会写入Cookies,以便在最后付款时提取信息。

1.2 Session

1.2.1 什么是Session

在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。

1.2.2 Session实现原理

服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机浏览器带session id过来了,就会使用内存中与之对应的session为之服务。

1.3 Token

1.3.1 什么是Token

token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。

当用户第一次登录后,服务器生成一个token并将此token返回给客户端,以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。

简单token的组成;uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token的前几位以哈希算法压缩成的一定长度的十六进制字符串。为防止token泄露)。

1.3.2 Token的原理

用户通过用户名和密码发送请求
程序校验
程序返回一个Token给客户端
客户端存储Token,并且每次发送请求携带Token
服务端验证Token,并返回数据

1.3.3 Token优点

1.支持跨域访问: Cookie是不允许垮域访问的,token支持;
2.无状态: token无状态,session有状态的;
3.去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在 你的API被调用的时候, 你可以进行Token生成调用即可;
4.更适用于移动应用: Cookie不支持手机端访问的;
5.性能: 在网络传输的过程中,性能更好;
6.基于标准化: 你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在 多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如: Firebase,Google, Microsoft)。

1.3.4 Token缺点

1.占带宽,正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10 万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多;
2.无法在服务端注销,那么久很难解决劫持问题;
3.性能问题,JWT 的卖点之一就是加密签名,由于这个特性,接收方得以验证 JWT 是否有效且被信任。但是大多数 Web 身份认证应用中,JWT 都会被存储到 Cookie 中,这就是说你有了两个层面的签名。听着似乎很牛逼,但是没有任何优势,为此,你需要花费两倍的 CPU 开销来验证签名。对于有着严格性能要求的 Web 应用,这并不理想,尤其对于单线程环境。

1.4 总结

token:三段式
      第一段:头:公司信息,加密方式
      第二段:荷载:真正的数据
      第三段:签名,通过第一段和第二段,通过某种加密方式加密得到的


token的使用分两个阶段
     登录成功后的签发token阶段-->生成三段
     登录成功访问某个接口验证阶段-->验证token是否合法

cookie是存在客户端浏览器的键值对
session是存在服务端的键值对
token是三段式,服务端生成的,存放在客户端(浏览器就放在cookie中,移动端,存在移动端中)

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

2. jwt

2.1 本质

在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token(本质就是token)认证机制。

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

token的应用于web方向的称之为jwt

2.2 构成和工作原理

JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
header:头
   声明类型,这里是jwt
   声明加密的算法,通常直接使用HMAC SHA256
完整的头部就像下面这样JSON:
   {
     'typ':'JWT',
     'alg':'HS256'
   }
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
payload:荷载
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
    标准中注册的声明
    公共的声明
    私有的声明
标准中注册的声明 (建议但不强制使用) :
    iss: jwt签发者
    sub: jwt所面向的用户
    aud: 接收jwt的一方
    exp: jwt的过期时间,这个过期时间必须要大于签发时间
    nbf: 定义在什么时间之前,该jwt都是不可用的.
    iat: jwt的签发时间
    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。

公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
然后将其进行base64加密,得到JWT的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature:签名
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64后的)
payload (base64后的)
secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // 
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBa
注意:secret是保存在服务器端的(加密方式+盐),jwt的签发生成也是在服务器端,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去,一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt了
jwt使用流程最核心的是:
  签发:登录接口签发
  认证:认证类认证

2.3 本质原理

jwt认证算法:签发与校验
1)jwt分三段式:头.体.签名 (head.payload.sgin)
2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
	"company": "公司信息",
	...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
	"user_id": 1,
	...
}
6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
	"head": "头的加密字符串",
	"payload": "体的加密字符串",
	"secret_key": "安全码"
}

签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token
校验:根据客户端带token的请求 反解出 user 对象

3. base64编码解码

3.1 本质

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

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

3.2 应用场景

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

3.3 代码演练

# base64的编码过程
import json
import base64

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)  # b'eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9'

# 把字符串转成bytes格式的两种方式
# res = base64.b64encode(info.encode('utf-8'))
# print(bytes(info,encoding='utf-8'))

# base64的解码过程
res = base64.b64decode('eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9')
print(res)  # b'{"name": "lqz", "userid": 6, "age": 19}'
# 把图片保存
s = 'iVBORw0KGgoAAAANSUhEUgAAAMcAAADHCAIAAAAiZ9CRAAAHFElEQVR42u3cwXajQAxEUf//T2eWs5iTsVE9qRF+LBObhuYS1CVOXj9ubvT2cgrculS9su3v7q58+NK3Phnr31/9Z6xPDuOjGSwd/H+Op2+i8CF+G1RVqlKVqjaquvb4hGYf51W7nOEpD/iojXXpdJD9qEpVqlLValW1B3btuMPrUTt/fD99g1L3YXgtPt+PqlSlKlV9oaqaBups+z4clndUuVkbi6rYVKUqValKVdS1D9ftYdnRF5f3zU9trDChUJWqVKWqb1M1cCZ4NB8eYSiPKoPwscIk6HzHRlWqUpWqRlRNZrX+5M4/SSSoyp+oyp8sUkVttaPER5+sPwbG6pte/BKoSlWqUtU6VdQqvS+hqPV38XZs2GbGw5Rw9PBMgbxKVapSlapurOpSKdCXZeNt77DVOlAGDYzVVOyqSlWqUtVGVX0LZrxCqs31pc/gb1OFhSN121CXUlWqUpWqnqRqMuGlxNRY1I6whvJINE8Vl4V0RlWqUpWqFqmiSoGwNMED4rtVP1SPnKJcq8Yuv1+lKlWpSlWrVPVNX185hWcfA/cGPqshOKRHripVqUpVi1RRbx3dZE1eC4gHLgzeDO5ruicfVpWqVKWqZ6jCr1AoGE/A+xrGA4FCn85kolSlKlWpaqOqcNKpx/yRuPxsD5hqVLAZQe3PiqpUpSpVLVLVF3wPhMh4FI4XPZM5/oDFt4OqSlWqUtUiVX2LYarsqOUIYTHXV1xSzYzJmZ9OFlSlKlWpakTVTVa8+IcnqygqtQ9x1+46qqeiKlWpSlXrVL2yLTxbfB7DQg2fjb4gvm+eyWRBVapSlao2qKqtMPvqGKpqwXvSVCrd96uBJsRvX1eVqlSlqo2qqAognIg+rwO4+1gMNObDD6tKVapS1V5VVHqLZ9nh8ji8E/qShb6edE0eMs+qUpWqVLVIVdgQpa5QXzQQ7qcvQD8yVl8rRVWqUpWqnqSKonO2yAgp45MQRgPI+r/DoqpUpSpVrVM1eanCeohqo+Je8bIsHItqDBQOTFWqUpWqFqmiioPaBcbnkaqQwgMLw4u+bJ26x1SlKlWp6tmqao9e6lcDTdwjKTl7FcH32GqRuqpUpSpVPUBVuBylAnRqiHCO+gLr8K01/K6jIn5VqUpVqtqrqq/RWzuTMKEYeP0ovEJUU5kK0JF+vKpUpSpVLVJFtS3x4olaVNeW2X1MB96vwq/p5WxdVapSlao2qAp3SkW0fWVQX1DSFx8MjNWUzqhKVapS1SJVYc2ENylrkxXGIlQFecQHXsiqSlWqUtW3qcLrGApK36TjZ3okIxg4U1WpSlWqeqSqsKQIDyVczU7G5ZNtgIGeNHVjq0pVqlLVk1TVKi28rTvQIp1cyVO3BBWgU83pN91lValKVaq6sSrqTPrmiKpR8CPsqykHeg9NzXtVqUpVqlqkCr8eeI0Spsl4K3oy8sCPGQ9cPq3WVaUqVanqxqrw93Xw9JbKLPDXoai8u4ap712uwi2qKlWpSlWLVFENUfzr1PO+b+GNh/5hXI4HCoWyVVWqUpWqFqmqLc771vZ4VdfX5A5vyJv8qjdbV5WqVKWqDarC3JyK1PGXhMJMvO/tpYFmMJXjAx0bValKVaraoIqa4nALay88vKBa7JRpql5k/xyoSlWqUtUiVeGLO+H1oODi7w9RKfnd4gO8Of2mY6MqValKVRtUhc973FDYjj17S1Bfp0KZkFe9rlKVqlSlqg2qJh/hkwXNZNmBvwUVVocDpa2qVKUqVT1JFfUIx7unVOiA7wenfGl+qEYF8tdEVapSlaoepgp/3YeyOFBy4YvzUFVffyK8OqpSlapU9SRVtQQciWh/Gv5Nw0ARNhn6h3d4U9WrKlWpSlUbVYXHXTum2qXCSxz83MNofmBawh1efmdBVapSlarupyp8GNcOpZbwUj7wKQ6bweHUUTOPpO2qUpWqVPUMVX1Tc6RTO4BpIP3va8MjkZCqVKUqVW1UdeTJTaXSR84ivAGoAiu8RdnRVaUqValqtaowR8BLAaor3NdqpcILql6c7EmrSlWqUtVeVfilor7OZr4di3zK60CmQ33r02xdVapSlao2qJp8Bk82g2uF2gvaqCwbH6v7vFSlKlWpaqMqKmjue0UJX28j/VTwwCa/Re2w3rFRlapUparTqiYLiDCRHzDUV8P1FanUtITXVFWqUpWq9qoKpyYssPByqhZV9C3O8T5xX4+8FouoSlWqUtUDVNUQUCzCioQagio38WY5nuNTDXVVqUpVqvpaVVTIjs/IfIf1Kp0jPfukeJqrq1SlKlWp6imqBiYiTBaogm+gvAtzfJy7qlSlKlU9QFWYreOP8IFCbfIsJhvYeABUTxZUpSpVqerGqvqW2X3xdC0Frs1srVg52yfG9/x2P6pSlapUtUiVmxtZhjoFbvj2BzQjIbj6aWtTAAAAAElFTkSuQmCC'

res = base64.b64decode(s)
with open('code.png', 'wb') as f:
    f.write(res)

4. drf-jwt

4.1 drf-jwt介绍

jwt:签发(登录接口)认证(认证类)
django中使用jwt
    1. 可以自己写
    2. https://github.com/jpadilla/django-rest-framework-jwt  (比较老)
    3. https://github.com/jazzband/djangorestframework-simplejwt (比较新)

安装:pip3 install djangorestframework-jwt

4.2 drf-jwt快速使用

1. 迁移表,因为它默认使用auth的user表签发token
2. 创建超级用户(auth的user表中要右记录)
3. 咱们不需要写登录接口,如果是使用auth的user表作为用户表,它可以快速签发
4. 签发(登录):只需要在路由中配置(因为它帮咱们写好登录接口来了)
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('login/', obtain_jwt_token),
]

4.3 登录认证

认证(认证类):导入,配置在视图上
前端访问时,token需要放在请求头中
Authorization:jwt token串
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated


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

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

4.4 drf-jwt修改返回格式

登录成功后,前端看到的格式,太固定了,只有token,我们想做成{code:100,msg:'登录成功,token:adafafsdaaa'}

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

使用步骤
1. 写一个函数
   def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'code': 100,
        'mag': '登录成功',
        'username': user.username,
        'token': token
    }
2. 把函数配置在配置文件中
   JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_response_payload_handler',
}

 以后登录接口返回的格式就是咱们写的函数的返回值

5. 自定义user表,签发token

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

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:zhy,id:1,过期时间}
            token = jwt_encode_handler(payload)  # 根据payload---》得到token:头.荷载.签名
            print(payload)  # {'user_id': 1, 'username': 'zhy', 'exp': 1665580212}
            print(token)  # eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InpoeSIsImV4cCI6MTY2NTU4MDIxMn0.1irBsYqwZoHA0MJN0IaWnOZL_T1Zqijq0cXhcdBhiA8

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

作业

1 快速签发

urls.py

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

2 修改返回格式

views.py

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

settings.py

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

3 自定义用户表签发token

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

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 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)
            print(payload)
            print(token)
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        except Exception as e:
            raise APIException('用户名或密码错误')

4 自定义认证类,验证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)

5 请求头中:X_FORWARDED_FOR 代之什么?

用户经过代理时,代理会增加这个字段,nginx可用内置变量$http_x_forwarded_for取到这个字段,没有使用代理时,此字段为空

当你使用了代理时,web服务器就不知道你的真实IP了,为了避免这个情况,代理服务器通常会增加一个叫做x_forwarded_for的头信息,把连接它的客户端IP(即你的上网机器IP)加到这个头信息里,这样就能保证网站的web服务器能获取到真实IP

通过名字就知道,X-Forwarded-For 是一个扩展头。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP,现在已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239 (Forwarded HTTP Extension)标准之中。

6 集群 ,分布式

集群

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

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

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

分布式

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

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

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

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

区别

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

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

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

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

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

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

集群和分布式都是由多个节点组成,但集群中各节点间基本不需要通信协调,而分布式中各个节点的通信协调是必不可少的。
posted @ 2022-10-12 21:56  |相得益张|  阅读(817)  评论(0编辑  收藏  举报