在11.保存登录状态与注销功能这篇文章中,我们使用了session来保存username,实际上Flask默认的session功能,是客户端session(client-side session),与之相对的是服务器端session(server-side session)。简单来说,这里的客户端session是将username加密后,以cookie的形式返还给客户端,客户端后续访问网站就携带着这个cookie,服务器解密cookie得到username。也就是说,如果知道了加密方法和cookie内容,就可以解密出username,这样是不够安全的。
今天我们来实现服务器端session(server-side session),原理很简单,实际上在【python socket编程】—— 5.实现cookie和session这篇文章中我们已经做过了。现在我们用数据库来保存session,并为其设置有效期。整个逻辑是这样的:用户登录后,生成一个随机字符串,将其作为session id,与对应的username和过期时间一起存入数据库,然后将session id作为cookie返回给客户端,客户端后续访问时,携带着含有session id的cookie,服务器通过cookie中的session id在数据库中检索session数据,判断登录状态。
建立一个sessions表(ORM模型)来存储session,字段如下:
class MySession(db.Model):
__tablename__ = 'sessions'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
# s_id代表session id,也是后面返回给客户端的cookie
s_id = db.Column(db.String(24), nullable=False)
username = db.Column(db.String(64), nullable=False)
start_time = db.Column(db.DateTime, nullable=False)
expire_time = db.Column(db.DateTime, nullable=False)
视图函数代码如下:
@app.route('/session/', methods=['GET', 'POST'])
def test_session():
username = None
if request.method == 'GET':
s_id = request.cookies.get('sid')
if s_id:
mysession = MySession.query.filter(MySession.s_id == s_id).first()
if mysession and mysession.expire_time > datetime.now():
username = mysession.username
return render_template('session.html', username=username)
else:
username = request.form.get('username')
s_id = random_strings(24)
now = datetime.now()
expire_time = now + timedelta(seconds=10)
mysession = MySession(s_id=s_id, username=username, start_time=now, expire_time=expire_time)
db.session.add(mysession)
db.session.commit()
response = make_response(redirect(url_for('test_session')))
response.set_cookie('sid', s_id)
return response
我们从POST方法讲起,当用户POST一个username过来时(为了简单,session.html只有用户名没有密码),生成一个随机字符串(random_strings是自己编写的一个小函数)作为session id,然后获取现在的时间now和过期时间expire_time(为了演示方便,过期时间是10秒之后),然后将这些信息存入MySession模型中,最后使用make_response方法生成一个response对象(这个对象有set_cookie方法,这也是Flask设置cookie的常规方法),并为其设置cookie,set_cookie第一个参数'sid'是key,第二个参数是value(session id),之后返回response对象。当请求是GET时候,首先就会使用request.cookies.get('sid')去获取cookie中的session id,如果获取到并且还在过期时间内,则向html传入username表明当前已经登录的用户。
最后html的内容如下:
Title{% if username %}
Welcome {{ username }}
Logout
{% else %}
Username:
Login
{% endif %}
效果展示:
未登录时:
登录后:
此时数据库中的session信息:
浏览器中的cookie内容:
过期之后浏览器仍然携带这个cookie,但刷新网页又变成未登录的状态了。