session 的用法
session 在 Flask 中通常用做设置某些页面的权限, 比如某些页面必须要登录才可以看到, 登录的信息或标志就放到 session 中. 它的使用过程如下:
在整个 flask 工程的启动文件中添加 App.config['SECRET_KEY'] = 'you never guess',SECRET_KEY 是用来加密 session 的, 本质上是一个加密盐.
再在使用的 py 文件中添加 from functools import wraps , 封装装饰器
在使用的 py 文件中添加 from flask import session
然后写处理函数
再在逻辑代码中写已经登录的标志或者是存储其他关于请求的信息
最后在设置限制的视图函数前添加限制函数的修饰器
一个简单的例子
- # encoding: utf-8
- from flask import Flask
- from flask import request, session, redirect
- from functools import wraps
- App = Flask(__name__)
- App.config['SECRET_KEY'] = 'you never guess' # 使用 session 必须要配置这个, 加密签名.
- # 登录, 注册认证函数
- def authorize(fn):
- @wraps(fn)
- def wrapper(*args, **kwargs): # 这里就像过滤器, 有了那个修饰器标志的视图函数都必须经过这个函数才可以返回请求
- user = session.get('logged_in', None) # 取得登录标志
- if user:
- return fn(*args, **kwargs) # 登录了就返回请求
- else:
- return 'need login!' # 否则就转到注册的页面
- return wrapper
- @App.route('/')
- @App.route('/home')
- def index():
- session["global_name"] = "global_path"
- return session["global_name"] + 'home.html'
- @App.route('/find')
- def find():
- print(vars(session))
- return session.get("global_name", "None") + 'find.HTML'
- @App.route('/doc')
- @authorize # 这个修饰器表示, 这个视图页面必须登录才可以访问
- def blog():
- print(session['username'])
- return 'blog.HTML'
- # 登录用户
- @App.route('/login', methods=['GET', 'POST'])
- def login():
- if request.method == 'GET':
- return "login.HTML"
- if request.method == 'POST':
- username = request.values.get('username')
- password = request.values.get('password')
- if username and password:
- session['logged_in'] = True # 登录成功
- session['username'] = username
- session['password'] = password
- return username + password
- else:
- return "need username and password"
- # 注销用户
- @App.route('/signout', methods=['GET', 'POST'])
- @authorize
- def logout():
- session['logged_in'] = False # 变成 false 就意味着需要重新登录了
- return redirect('/home')
- if __name__ == '__main__':
- App.run()
几句 curl
- # 登录接口与返回, 可以看出 response 中 set-cookie 了
- curl -X POST "http://127.0.0.1:5000/login" -d "username=test&password=123"
- HTTP/1.0 200 OK
- Set-Cookie: session=eyJsb2dnZWRfaW4iOnRydWUsInBhc3N3b3JkIjoiMTIzIiwidXNlcm5hbWUiOiJsZ2oifQ.DoNGjg.4c2Adke_tzqo5MW_BHs95FvY6i4; HttpOnly; Path=/
- # 不带 cookie 访问需要登录的接口
- curl "http://127.0.0.1:5000/doc"
- need signin!
- # 带上 cookie 后访问通过
- curl --cookie "session=eyJsb2dnZWRfaW4iOnRydWUsInBhc3N3b3JkIjoiMTIzIiwidXNlcm5hbWUiOiJsZ2oifQ.DoNGjg.4c2Adke_tzqo5MW_BHs95FvY6i4" "http://127.0.0.1:5000/doc"
- user/blog.HTML
- # 登出接口也是同上的.
- curl "http://127.0.0.1:5000/logout"
- need signin!
- curl --cookie "session=eyJsb2dnZWRfaW4iOnRydWUsInBhc3N3b3JkIjoiMTIzIiwidXNlcm5hbWUiOiJsZ2oifQ.DoNGjg.4c2Adke_tzqo5MW_BHs95FvY6i4" "http://127.0.0.1:5000/signout"
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
- <title>Redirecting...</title>
- <h1>Redirecting...</h1>
- <p>You should be redirected automatically to target URL: <a href="/home">/home</a>. If not click the link.
- # 使用 session 存储一些其他信息也是依赖于 cookie 存在的
- curl -v "http://127.0.0.1:5000"
global_pathhome.HTML
- HTTP/1.0 200 OK
- Set-Cookie: session=eyJnbG9iYWxfbmFtZSI6eyIgYiI6IloyeHZZbUZzWDNCaGRHZz0ifX0.DoNPag.ooEMHsinZlKRQpLF_-S3axsH3jc; HttpOnly; Path=/
- # 想要得到设置 global_name, 不带 cookie 是得不到的
- curl "http://127.0.0.1:5000/find"
Nonefind.HTML
- # 带 cookie
- curl --cookie "session=eyJnbG9iYWxfbmFtZSI6eyIgYiI6IloyeHZZbUZzWDNCaGRHZz0ifX0.DoNPag.ooEMHsinZlKRQpLF_-S3axsH3jc" "http://127.0.0.1:5000/find"
global_pathfind.HTML
- # 设置 cookie
- curl -v "http://127.0.0.1:5000/set_cookie"
- < HTTP/1.0 200 OK
- < Set-Cookie: name=test; Expires=Wed, 19-Sep-2018 03:42:31 GMT; Max-Age=200; Path=/
- < Set-Cookie: age=18; Path=/
- set_cookie* Closing connection 0
- # 获取 cookie
- curl --cookie "name=test;age=18" "http://127.0.0.1:5000/get_cookie"
- name is test,name is 18
总结:
平时使用浏览器访问接口, 很难注意到 cookie 和 session 的区别与联系, 使用 curl 就把区别暴露了出来. 上面的 curl 交互可以看出 session 是会以 set-cookie 的方式设置到浏览器中, 之后的访问都会将 cookie 带上请求其他接口, 若使用 curl 而不带上 cookie 就会导致错误, 这也是为什么说 session 是依赖 cookie 存在的.
cookie 存在的目的:
因为 http 协议属于无状态协议, 它不跟踪从一个客户端到另一个客户端的请求信息. 也就是说即使第一次和服务器连接后并且登录成功, 第二次请求服务器依然不知道请求的是哪个用户. 所以使用 cookie 来解决这个问题: 第一次登录成功后, 服务器返回 cookie 给浏览器, 然后浏览器保存在本地, 当用户发送第二次请求时, 就会自动的把上次请求存储的 cookie 数据携带给服务器, 服务器再根据 cookie 数据判断是哪个用户.
session 和 cookie 的作用类似: 也是用来存储用户相关的信息, 存储在服务器端. 把用户的信息经过加密后存储在 session 中, 然后产生一个唯一的 session_id.
cookie 和 session 的结合使用
一般有两种存储方式:
(1) 存储在服务器端: 通过 cookie 存储一个 session_id, 然后具体的数据则是保存在 session 中. 如果用户已经登录, 则服务器会在 cookie 中保存一个 session_id, 下次再请求时, 会把该 session_id 携带上来, 服务器根据 session_id 在 session 库中获取用户的 session 数据, 就知道用户到底是谁了. 以及之前保存的一些状态信息, 这种专业术语叫做 server side session.
(2) 存储在客户端: 将 session 数据加密, 然后存储在 cookie 中. 这种专业术语叫做 client side session.flask 框架采用的就是这种方式, 但是可以替换成其他形式.
flask 的 session 机制
把用户信息经过加密后放入到 session 中, 然后把 session 存放到 cookie 中. 下次请求时, 从浏览器发送过来的 cookie 中读取到 session, 然后再从 session 中读取数据并解密, 获取最终的用户数据. 这样可以节省服务器开销.
cookie 的删除
1) 可以通过在浏览器中设置来清除 cookie.
(2) 使用 Response 的 set_cookie 进行清除
- @App.route('/del_cookie')
- def del_cookie():
- response=make_response('delete cookie')
- response.set_cookie('Name','',expires=0)
- return response
(3)使用 Response 的 delete_cookie 方法.
- @App.route('/del_cookie2')
- def del_cookie2():
- response=make_response('delete cookie2')
- response.delete_cookie('Name')
- return response
cookie 的其他属性
观察 "Set-Cookie: name=test; Expires=Wed, 19-Sep-2018 03:42:31 GMT; Max-Age=200; Path=/" 可以看出其实 cookie 有很多属性, 如下:
在 Chrome 控制台中的 resources 选项卡中可以看到 cookie 的信息.
一个域名下面可能存在着很多个 cookie 对象.
name 字段为一个 cookie 的名称.
value 字段为一个 cookie 的值.
domain 字段为可以访问此 cookie 的域名.
非顶级域名, 如二级域名或者三级域名, 设置的 cookie 的 domain 只能为顶级域名或者二级域名或者三级域名本身, 不能设置其他二级域名的 cookie, 否则 cookie 无法生成.
顶级域名只能设置 domain 为顶级域名, 不能设置为二级域名或者三级域名, 否则 cookie 无法生成.
二级域名能读取设置了 domain 为顶级域名或者自身的 cookie, 不能读取其他二级域名 domain 的 cookie. 所以要想 cookie 在多个二级域名中共享, 需要设置 domain 为顶级域名, 这样就可以在所有二级域名里面或者到这个 cookie 的值了.
顶级域名只能获取到 domain 设置为顶级域名的 cookie, 其他 domain 设置为二级域名的无法获取.
path 字段为可以访问此 cookie 的页面路径. 比如 domain 是 abc.com,path 是 / test, 那么只有 / test 路径下的页面可以读取此 cookie.
expires/Max-Age 字段为此 cookie 超时时间. 若设置其值为一个时间, 那么当到达此时间后, 此 cookie 失效. 不设置的话默认值是 Session, 意思是 cookie 会和 session 一起失效. 当浏览器关闭(不是浏览器标签页, 而是整个浏览器) 后, 此 cookie 失效.
Size 字段 此 cookie 大小.
http 字段 cookie 的 httponly 属性. 若此属性为 true, 则只有在 http 请求头中会带有此 cookie 的信息, 而不能通过 document.cookie 来访问此 cookie.
secure 字段 设置是否只能通过 https 来传递此条 cookie
下面是 flask 中 cookie 对应属性的配置项
SECRET_KEY 密钥
SESSION_COOKIE_NAME 会话 cookie 的名称
SESSION_COOKIE_DOMAIN 会话 cookie 的域. 如果没有设置的话, cookie 将会对 SERVER_NAME 所有的子域都有效.
SESSION_COOKIE_PATH 会话 cookie 的路径. 如果没有设置或者没有为 '/' 设置, cookie 将会对所有的 APPLICATION_ROOT 有效.
SESSION_COOKIE_HTTPONLY 控制 cookie 是否应被设置 httponly 的标志, 默认为 True .
SESSION_COOKIE_SECURE 控制 cookie 是否应被设置安全标志, 默认为 False.
扩展
既然 session 依赖 cookie, 要是浏览器禁用了 cookie 改怎么达到状态保持的效果呢? 这个时候, 就需要用到 URL 重写了, 既让服务器收到的每个请求参数中都带有 sessioinId, 也就是从原本的隐式 (headers 传参) 变为 url 或 body 传参.
当我们清空浏览器的时候, session 会消失吗?
这个问题包含着一些陷阱. 因为很多时候当我们清空浏览器以后, 确实需要重新登录系统才可以操作, 所以很多人自然而然认为清空浏览器缓存 (包含 cookie) 以后 session 就会消失.
其实这种结论是错误的. 要知道, session 是存在于服务器的, 你清除浏览器缓存, 只是清除了 cookie, 跟 session 一点关系都没有. 那么为什么我们却不能访问网站, 而需要重新登录了呢? 因为清空了浏览器缓存, 这时候 cookie 数组中必定不会有 JSESSIONID 这个 cookie, 所以必须得新建一个 session, 用新的 sessionId 来给 JSESSIONID 这个 cookie 赋值. 由于是新建的 session,session 中必定没有 userId 这样的属性值, 所以判断结果自然为空, 所以需要重新登录. 这次赋值以后, 下一次再请求该网站的时候, 由于 cookie 数组中已经有了 JSESSIONID 这个 cookie, 并且能通过该 JSESSIONID 的值找到相应的 session, 所以就不需要再重新登录了.
参考
- http://www.pythondoc.com/flask/config.HTML
- https://blog.csdn.NET/qq_37526590/article/details/80219227
- https://www.cnblogs.com/keyi/p/6823853.HTML
来源: https://juejin.im/entry/5ba1e2f96fb9a05cfe486509