cookie和session结合使用:web开发发展至今,cookie和session的使用已经出现了一些非常成熟的方案。
session的两种存储方式
如今的市场或者企业里,一般有两种存储方式:
- 存储在服务端:通过cookie存储一个session_id,然后具体的数据则是保存在session中。如果用户已经登录,则服务器会在cookie中保存一个session_id,下次再次请求的时候,会把该session_id携带上来,服务器根据session_id在session库中获取用户的session数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做server side session。
- 将session数据加密,然后存储在cookie中。这种专业术语叫做client side session。flask采用的就是这种方式,但是也可以替换成其他形式。
从浏览器发送请求到服务器返回响应的过程分析flask 是如何通过cookie和session机制实现用户是否登录的
Flask中的session是通过from flask import session。然后添加值key和value进去即可。并且,Flask中的session机制是将session信息加密,然后存储在cookie中。专业术语叫做client side session。其具体实现步骤如下:
- 设置秘钥
- 设置过期时间 也可以不设置
- 在用户首次登录成功后以key-value的方式向session中添加数据,比如加入session【‘username’】 = username
- 根据秘钥和加密算法将session数据进行加密
- 将加密后的session数据添加到response中,保存在set-cookie 字段中
- 浏览器接受到服务器返回的消息,通过解析response header中的set-cookie字段,自动将cookie信息保存在浏览器中。
- 当用户再次在同一浏览器,同一域名下发送请求时,浏览器会将cookie自动添加在request header的cookie字段中。
- 服务器获取到request header中的cookie数据,判断其是否失效,如果没有失效则按照算法对其解密,如果cookie数据中途被修改过,则会导致解密失败,该cookie将会直接被丢弃,如果解析cookie成功,则可以将session中的key-value键值对解析出来,然后可以通过判断该键值对中是否有key== ‘username’的字段,如果有说明用户已经登录,如果没有说明用户没有登录。
细节介绍
上述过程涉及到加密算法,秘钥生成等细节,下面对这些细节详细介绍下:
Flask生成生成动态秘钥SECRET_KEY并设置秘钥
import os
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = os.urandom(24)
'\xca\x0c\x86\x04\x98@\x02b\x1b7\x8c\x88]\x1b\xd7"+\xe6px@\xc3#\\'
给session赋值
@app.route('/login/', methods=['GET', 'POST'])
def login():
# 如果是登录 验证用户名密码则 进入 post
if request.method == 'POST':
if not request.form.has_key('username') or not request.form.has_key('password') or request.form["username"] == "":
return returnInfo(0, '参数异常:缺少参数')
password = request.form['password']
username = request.form['username']
login_time = int(time.time())
user = User(username, password, login_time, db)
result = user.check_user_can_logged()
if result['status']:
session['username'] = username
return json.dumps(result)
# 加载登录页面 get 请求
return render_template('login.html')
获取session的值
@app.before_request
def filter_url():
# 如果用户未登录,则跳转到用户登录界面 登录请求和请求静态资源除外
if 'username' not in session and request.path != url_for('login') and request.path.find('/static/') == -1:
return redirect(url_for('login'))
如何删除session数据
删除一个:session.pop(“key”,none),例如上述案例中为:session.pop(“username”,none)
删除所有:session.clear()
源码分析
在获取 cookie 数据的过程中,最核心的几句话是:
s = self.get_signing_serializer(app)
val = request.cookies.get(app.session_cookie_name)
data = s.loads(val, max_age=max_age)
return self.session_class(data)
其中两句都和 s 有关,signing_serializer 保证了 cookie 和 session 的转换过程中的安全问题。如果 flask 发现请求的 cookie 被篡改了,它会直接放弃使用。
我们继续看 get_signing_serializer 方法:
def get_signing_serializer(self, app):
if not app.secret_key:
return None
signer_kwargs = dict(
key_derivation=self.key_derivation,
digest_method=self.digest_method
)
return URLSafeTimedSerializer(app.secret_key,
salt=self.salt,
serializer=self.serializer,
signer_kwargs=signer_kwargs)
我们看到这里需要用到很多参数:
- secret_key:密钥。这个是必须的,如果没有配置 secret_key 就直接使用 session 会报错
- salt:为了增强安全性而设置一个 salt 字符串(可以自行搜索“安全加盐”了解对应的原理)
- serializer:序列算法
- signer_kwargs:其他参数,包括摘要/hash算法(默认是 sha1)和 签名算法(默认是 hmac)
URLSafeTimedSerializer 是 itsdangerous 库的类,主要用来进行数据验证,增加网络中数据的安全性。itsdangerours提供了多种 Serializer,可以方便地进行类似 json 处理的数据序列化和反序列的操作。至于具体的实现,因为篇幅限制,就不解释了。