博主最近在学习flask的过程中,使用session进行登录认证的时候遇到了以下场景:
客户端禁用cookies 的时候如何使用session进行登录验证?
因为session的大致实现流程为:服务器端通过cookie获取sessionid,从而获取到session,如果cookie被禁用,服务器端则无法获取sessionid,从而认为这次一个全新的请求,从而生成新的session。
在java serverlet中可以通过重写url在服务器端获取session,但是在flask中博主没有发现这一用法,查阅资料的时候发现可以通过重写flask的session_interface来重写session的实现机制,自定义获取sessionid的途径,进而获取session
本文将重写,flask的session模块,模拟通过url获取sessionid,通过(header,body)等可以以此为据自行扩展。
my_session.py
首先定义一实体类,多继承dict,和SessionMixi类,并重写父类 __setitem__、__getitem__、__delitem__方法。
import uuid
import json
from flask.sessions import SessionMixin, SessionInterface
from itsdangerous import Signer, BadSignature, want_bytes
class MySession(dict, SessionMixin):
def __init__(self, initial=None, sid=None):
self.sid = sid
self.initial = initial
super(MySession, self).__init__(initial or ())
def __setitem__(self, key, value):
super(MySession, self).__setitem__(key, value)
def __getitem__(self, item):
return super(MySession, self).__getitem__(item)
def __delitem__(self, key):
super(MySession, self).__delitem__(key)
然后定义一实体类,继承SessionInterface类,重写其open_session方法(用来创建session)、save_session方法(用来保存和下发session)代码如下:
class MySessionInterface(SessionInterface): session_class = MySession container = {} def __init__(self): import redis self.redis = redis.Redis(host='10.79.148.226') def _generate_sid(self): return str(uuid.uuid4().hex) def _get_signer(self, app): if not app.secret_key: return None return Signer(app.secret_key, salt='salt!', key_derivation='hmac') def open_session(self, app, request): # sid = request.cookies.get(app.session_cookie_name) sid = request.args.get('sid') if not sid: sid = self._generate_sid() return self.session_class(sid=sid) signer = self._get_signer(app) try: sid_as_bytes = signer.unsign(sid) sid = sid_as_bytes.decode() except BadSignature: sid = self._generate_sid() return self.session_class(sid=sid) val = self.container.get(sid) if val is not None: try: data = json.loads(val) return self.session_class(data, sid=sid) except: return self.session_class(sid) return self.session_class(sid=sid) def save_session(self, app, session, response): print('save_session') domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) val = json.dumps(dict(session)) self.container.setdefault(session.sid, val) session_id = self._get_signer(app).sign(want_bytes(session.sid)) print(session_id) response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
在产生http请求的时候,服务器端首先会获取sessionid,然后通过对sessionid的判断,在session的存储空间中获取or生成session。
扫描二维码关注公众号,回复: 4161225 查看本文章flask中取得session的过程如下:
- 获取session签名算法
- 获取session值
- 从签名算法取值
因为flask源码中,session的信息是保存在客户端的cookie中,所以需要从cookie中获取session,然后进行解密。
而本文则将session存储在服务器的内存中,然后通过获取sessionid,从而从字典中获取session。
本文通过request.args.get('sid')即可通过url获取sessionid,进而取出session。
同样也可以将sessionid,通过header、body等进行传递,只要自行编写相应获取sessionid 的方法,并且从session的存储介质中获取session即可。
flask中存储session额过程如下:
- 判断此时session是否为空,如为空删除对应cookie
- 判断session是否变化,如果没有变化则直接跳过,如果变化生成变化之后session
- 将session信息进行加密,然后通过客户端的cookie进行存储
flask源码中session的存储为,将加密过后的session信息全部存储在客户端的cookie中,一旦客户端cookie被禁用,服务器端无论如何都无法获取session信息,只能把每一次请求都认为是新的请求然后不断的生成新的session。
其实现方式与token大致相同。
而本文的实现方式则是通过,在服务器端开辟存储空间,然后将sessionid,通过cookie、response、header的方法传递给前端,然后前端将获取的sessionid存储在localstorage中,从而实现实现了sessionid 的存储。当再次发起请求的时候,从localstorage将获取到的sessionid,通过url或者header传到后端,然后实现了禁用cookie使用session进行认证等功能。
app.py
# -*- coding: utf-8 -*-
from flask import Flask, jsonify, session, make_response, request
from werkzeug.wrappers import Response
from my_session import MySessionInterface
app = Flask(__name__)
app.secret_key = 'please-generate-a-random-secret_key!'
app.session_interface = MySessionInterface()
app.config.from_object('setting')
@app.route('/')
def hello_world():
print(session)
session['a'] = 'a'
print(request.cookies)
print(session)
return 'hello world'
if __name__ == '__main__':
app.run()
只要将app.session_interface 指定为自定的SessionInterface即可。
因为可以通过重写session的实现方法,从而控制session的获取与存储,我们也可以将session序列化之后存储在redis、memcached中,比如在使用Nginx做负载均衡的时候,就可以将多台服务器的session指定到同一个session存储的介质中,从而解决负载均衡时session认证失败的问题。