Django缓存机制cache和token会话存储

时间:2023-04-01 12:14:09


Django缓存机制和token存储

  • 缓存
  • 配置缓存
  • 创建缓存数据表并迁移文件到文件
  • Django中使用缓存
  • 局部缓存
  • 缓存api的使用
  • 浏览器缓存策略
  • 强缓存
  • 协商缓存
  • token生成
  • token基本知识
  • JWT
  • header
  • Payload
  • Signature
  • 服务端生成Token
  • 加密算法
  • Token如何存储

缓存

定义:缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存

比如redis数据库

意义:视图渲染有一定成本,数据库的频繁查询过高;所以对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数;用户拿到响应的时间成本会更低

given a URL,try finding that page in the cache 

if the page is in the cache :
	return the cached page
else:
	generate the pagel
	save the generated page in the cache (for next time)
	return the generated page

配置缓存

# 数据库缓存
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOACTION': 'my_cache_table',   # 表名
        'TIMEOUT': 300, # 缓存保存时间单位:秒,默认300
        'OPTIONS': {
            'MAX_ENTRIES': 300, # 缓存最大数据条数
            'CULL_FREQUENCY': 2,    # 缓存条数达到最大值时 删除1/X的缓存数据
        }
    }
}

# 本地内存缓存
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}

# 文件系统缓存
CACHES = {
    'default': {
        'BACKEND': 'django.core.caches.backends.filebased.FileBasedCache',
        'LOCATION': '/var/temp/django_cache',   # Linux中文件夹路径
    }
}

创建缓存数据表并迁移文件到文件

python manage.py createcachetable
python manage.py makemigrations
python manage.py migrate

Django中使用缓存

# views.py
from django.views.decorators.cache import cahce_page

@cache_page(30)	-> 单位s
def my_view(request):
	pass
# urls.py
from django.views.decorators.cache import cahce_page

urlpatterns = {
	path('', cache_page(60)(my_view)),
}

局部缓存

from django.core.cache import cache

cache1 = caches['myalias']
cache2 = caches['myalias_2']

缓存api的使用

cache.set(key, value, timeout)-存储缓存

key:缓存的key,字符串类型
value:Python对象
timeout:缓存存储时间(s),默认为QACHES中的TIMEOUT值
返回值: None

cache.get(key)- 获取缓存

key:缓存的key
返回值:为key的具体值,如果没有数据,则返回None

cache.add(key, value)-存储缓存,只在key不存在时生效

返回值: True[存储成功]or False[存储失败]

cache.get_or_set(key, value, timeout)-如果未获取到数据则执行set操作

返回值: value

cache.set_many(dict,timeout)-批量存储缓存

dict: key和value的字典
timeout:存储时间(s)
返回值:插入不成功的key的数组

cache.get_many(key_list)– 批量获取缓存数据

key_list:包含key的数组
返回值:取到的key和value的字典

cache.delete(key) -删除key的缓存数据

返回值: None

cache.delete_many(key_list)- 批量删除

返回值: None

浏览器缓存策略

Django缓存机制cache和token会话存储

强缓存

不会向服务器发送请求,直接从缓存中读取资源

  • 响应头- Expires
    定义:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点样例: Expires:Thu, 02 Apr 2030 05:14:08 GMT
  • 响应头– Cache-Control
    在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当`Cache-Control:max-age=120代表请求创建时间后的120秒,缓存失效
    说明:目前服务器都会带着这两个头同时响应给浏览器,浏览器优先使用Cache-Control

协商缓存

强缓存中的数据一旦过期,还需要跟服务器进行通信,从而获取最新数据;思考?如果强缓存的数据是一些静态文件,大图片等;
解答:考虑到大图片这类比较费带宽且不易变化的数据,强缓存时间到期后,浏览器会去跟服务器协商,当前缓存是否可用,如果可用,服务器不必返回数据,浏览器继续使用原来缓存的数据,如果文件不可用,则返回最新数据

token生成

整个基于Token的验证流程如下:

  • 客户端使用用户名跟密码请求登录
  • 服务器收到请求,去验证用户名和密码
  • 验证成功后,服务端会签发一个Token,再把这个Token发送到客户端
  • 客户端收到的Token以后可以把它存储起来,比如放在Cookie或LocalStorage里
  • 客户端每次向服务器发送其他请求的时候都要带着服务器签发的Token
  • 服务器收到请求,去验证客户端请求里面带着的Token,如果验证成功,就像客户端返回请求的数据

token基本知识

JWT

构造Token的方法挺多的,可以说只要是客户端和服务器端约定好了格式,是想怎么写就怎么写的,然而还有一些标准写法,例如JWT读作/jot/,表示:JSON Web Tokens.
JWT标准的Token有三个部分:

  • header
  • payload
  • signature

三个部分会用点分割开,并且都会使用Base64编码,所以真正的Token看起来像这样

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

header

header部分主要是两部分内容,一个是Token的类型,另一个是使用的算法,比如下面的类型就是JWT,使用的算法是HS256:

{
  "typ": "JWT",
  "alg": "HS256"
}

上面的内容要用 Base64 的形式编码一下,所以就变成这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:

  • iss:Issuer,发行者
  • sub:Subject,主题
  • aud:Audience,观众
  • exp:Expiration time,过期时间
  • nbf:Not before
  • iat:Issued at,发行时间
  • jti:JWT ID

Signature

JWT的最后一部分是Signature,这部分相当于前两段的摘要,用来防止其他人来篡改Token中的信息,在处理时可以首先将前两段生成的内容使用Base64生成一下再加盐然后利用MD5等摘要算法在生成一遍

服务端生成Token

在服务端生成Token的时候,需要解决两个问题

  1. 使用什么加密算法
  2. Token如何存储

加密算法

这里的加密算法并不是MD5,SHA1这样的哈希算法,因为这种算法是无法解密的,只能用来生成摘要,在Django中内置了一个加密前面模块django.core.signing模块,可以用来加密和解密任何数据,使用签名模块的dumps和load函数来实现

from django.core import signing
value = signing.dumps({"foo":"bar"})
src = signing.loads(value)
print(value)
print(src)

Token如何存储

在服务器中Token可以存储在内存中,因为本质是字符串,所以并不会占用很大的内存空间,如果是分布式的存储可以将所有的token信息分段存储在不同的服务器中,也可以存储在数据库中,在Django中提供了缓存类,可以用来存储Token,Django的缓存可以结合Redis来使用,可以借助django-redis来实现

安装

pip install django-redis

为了使用django-redis,需要将django cache setting修改,修改settings.py,默认是没有Cache的配置信息的,在其中添加:

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

当然,需要你首先安装了redis

import time
from django.core import signing
import hashlib
from django.core.cache import cache

HEADER = {'typ': 'JWP', 'alg': 'default'}
KEY = 'CHEN_FENG_YAO'
SALT = 'www.lanou3g.com'
TIME_OUT = 30 * 60  # 30min


def encrypt(obj):
    """加密"""
    value = signing.dumps(obj, key=KEY, salt=SALT)
    value = signing.b64_encode(value.encode()).decode()
    return value


def decrypt(src):
    """解密"""
    src = signing.b64_decode(src.encode()).decode()
    raw = signing.loads(src, key=KEY, salt=SALT)
    print(type(raw))
    return raw


def create_token(username):
    """生成token信息"""
    # 1. 加密头信息
    header = encrypt(HEADER)
    # 2. 构造Payload
    payload = {"username": username, "iat": time.time()}
    payload = encrypt(payload)
    # 3. 生成签名
    md5 = hashlib.md5()
    md5.update(("%s.%s" % (header, payload)).encode())
    signature = md5.hexdigest()
    token = "%s.%s.%s" % (header, payload, signature)
    # 存储到缓存中
    cache.set(username, token, TIME_OUT)
    return token


def get_payload(token):
    payload = str(token).split('.')[1]
    payload = decrypt(payload)
    return payload


# 通过token获取用户名
def get_username(token):
    payload = get_payload(token)
    return payload['username']
    pass


def check_token(token):
    username = get_username(token)
    last_token = cache.get(username)
    if last_token:
        return last_token == token
    return False