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
浏览器缓存策略
强缓存
不会向服务器发送请求,直接从缓存中读取资源
- 响应头- 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的时候,需要解决两个问题
- 使用什么加密算法
- 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