flask与session

1. 什么是session

session是一种机制,是识别用户身份的机制,服务端给客户端一个session ID, 此后这个session ID就唯一的代表这个用户。现在有两个问题需要考虑:

  1. 服务端什么时候给客户端发放session ID呢?
  2. 服务端怎么才能告知客户端给他的session ID是什么呢,客户端再次发起请求时又怎么告知服务端自己的session ID呢?

通常来说,当你登录一个web服务时,它就会给你发放一个session ID,在你登录后,你做的操作都是对你这个用户生效的,如果你没有登录,这些操作是不被允许的,因此后端要根据session ID来判断是你否已经登录。此外,服务端也会在session里存储你的一些关键数据,比如你的用户id,姓名,等等,这些信息虽然可以从数据库里查询,但是保存在session里相比于保存在数据库里更容易获取。

记下来看第二个问题,服务端告知客户端session ID,是通过cookie进行的,同样,客户端的请求里也会通过cookie把session ID发送给服务端。

关于session,网上讨论的文章非常多,但很长时间里,我都搞不懂它是个什么东西,因为太多的文章都在人云亦云,反复抄来抄去,以至于大家讲的东西都很接近但又重来不给具体实例,咱们这篇文章就要打破这个不好的习惯。

2. 对session理解更进一步

很长一段时间里,我对session的理解都非常模糊,模糊主要体现在以下两点:

  1. session ID 是怎样生成的
  2. session 的数据保存在哪里

先看第一个问题,session ID怎样生成,生成是一个动词,做这个动作的主体是谁呢?是服务端,如果你的服务是java的,用tomcat部署,那么session ID shi tomcat生成的,如果你的服务是flask编写的,那么session ID是flask框架生成的,在response里,通过Set-Cookie 首部传给前端,虽然我们叫它session ID,但在cookie里,它的key却并没有明确的规范,因此你能看到SESSION,也能看到SESSIONID,tomcat生成的叫JSESSIONID。

使用session的目的是为了保存用户相关的少量重要信息,避免频繁的查询数据库,那么session的数据保存在哪里呢?有4中保存方法

  1. 内存
  2. 本地文件
  3. 数据库
  4. cookie

保存在内存中是一个可行的方案,但如果服务挂掉了,所有用户的session也就丢失了。

保存在本地文件比内存安全一些,但对于多个节点的web服务会遇到session共享问题,你在A服务器上保存了小明的session数据,但是小明下一次的请求来到了B服务器上,而B服务器上没有小明的session信息,这种情况可以根据用户的IP将请求路由到固定的服务节点上解决。

保存到数据库,即安全,又能实现session共享。保存到cookie里,也是一个可行的方案,flask框架就是这样做的。

3. flask 登录请求里使用session

我们使用flask 做一个简单的web,设计一个登录接口,用户只有登录以后才能访问其他页面,我在session里保存用户的username,如果session里没有username则不允许用户访问其他页面。

from flask import Flask, session

app = Flask(__name__)

app.config['SECRET_KEY'] = 'hard to gusss'


@app.route('/login/<string:username>')
def name_view(username):
    session['username'] = username
    return 'ok'

@app.route('/')
def get_name():
    if 'username' not in session:
        return 'not login'
    else:
        return 'welcome ' + session['username']


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5500)

使用flask的session时,需要设置SECRET_KEY,flask的session数据保存在cookie里,而cookie是明文的,因此需要对session数据进行加密,SECRET_KEY就是用来加密的,如果泄露了,你的session就不安全了。

服务启动,如果你直接访问http://127.0.0.1:5500/, 得到的是not login,这说明你还没有登录。想要登录,需要访问login接口,比如下面的地址

http://127.0.0.1:5500/login/sheng

真正的登录应该使用post请求,本例子为了方便,使用get请求来模拟登录,用户名是sheng,后端也没有加什么处理逻辑,认为登录是成功的,将username设置到session里,这时你可以通过浏览器的开发者工具来观察后端的响应,会有一个 Set-Cookie

Set-Cookie: session=eyJ1c2VybmFtZSI6InNoZW5nIn0.YWaAyg.EWTHe6YexZ6SYClFztPj1aK1PBs; HttpOnly; Path=/

在flask框架里,session ID在cookie里用session做key来保存,前面已经讲过,具体的名称是没有规范的。flask框架的特别之处在于,session ID还承担了保存数据的功能,这便是说,eyJ1c2VybmFtZSI6InNoZW5nIn0.YWaAyg.EWTHe6YexZ6SYClFztPj1aK1PBs里隐藏着username=sheng这条信息,只是它被加密了,你看不出来。

好了,已经登录成功,接下来,再次访问http://127.0.0.1:5500/, 你得到了welcome sheng, username=sheng这条信息又被客户端通过cookie传回给了服务端。

4. flask session的一般操作

对于session,可以有以下几个操作

  1. 读取session
  2. 设置session
  3. 删除session
  4. 清空session

这几种操作都比较简单,我提供示例代码

session['username'] = 'sheng'  # 设置sesion

username = session['username']  # 获取值,如果不存在,会报错
username = session.get('username')  # 获取值,不存在返回None,不会报错,类似字典的get

session.pop('username')  # 删除session

session.clear   # 清空所有session

5. 设置falsk session的过期时间

  1. 如果不进行任何设置,当浏览器关闭后,session就会过期
  2. 如果设置session.permanent = True, 则过期时间为31天
  3. 通过PERMANENT_SESSION_LIFETIME 更灵活的设置过期时间

下面的示例,我设置session的过期时间是1分钟

from flask import Flask, session
from datetime import timedelta
app = Flask(__name__)

app.config['SECRET_KEY'] = 'hard to gusss'
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=60)

@app.route('/login/<string:username>')
def name_view(username):
    session['username'] = username
    # session.permanent = True
    return 'ok'

@app.route('/')
def get_name():
    if session.get('username') is None:
        return 'not login'
    else:
        return 'welcome ' + session['username']


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5500)

如果通过配置PERMANENT_SESSION_LIFETIME设置session过期时间,session.permanent = True可以不执行,设置permanent为True的目的是让浏览器记住过期时间,如果不设置,你在浏览器里查不到session的过期时间,但过期时间依然是生效的。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案