python web开发 Flask+禁用cookies+session

博主最近在学习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 查看本文章

参考文档:https://cizixs.com/2017/03/08/flask-insight-session/

flask中取得session的过程如下:

  1. 获取session签名算法
  2. 获取session值
  3. 从签名算法取值

因为flask源码中,session的信息是保存在客户端的cookie中,所以需要从cookie中获取session,然后进行解密。

而本文则将session存储在服务器的内存中,然后通过获取sessionid,从而从字典中获取session。

本文通过request.args.get('sid')即可通过url获取sessionid,进而取出session。

同样也可以将sessionid,通过header、body等进行传递,只要自行编写相应获取sessionid 的方法,并且从session的存储介质中获取session即可。

flask中存储session额过程如下:

  1. 判断此时session是否为空,如为空删除对应cookie
  2. 判断session是否变化,如果没有变化则直接跳过,如果变化生成变化之后session
  3. 将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认证失败的问题。

猜你喜欢

转载自blog.csdn.net/weixin_41294853/article/details/84035302
今日推荐