在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
的内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
{% if username %}
<p>Welcome {{ username }}</p>
<button>Logout</button>
{% else %}
<form action="" method="post">
<p>Username: <input type="text" name="username"></p>
<button>Login</button>
</form>
{% endif %}
</div>
</body>
</html>
效果展示:
未登录时:
登录后:
此时数据库中的session信息:
浏览器中的cookie
内容:
过期之后浏览器仍然携带这个cookie
,但刷新网页又变成未登录的状态了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。