Flask下的session认证和cache header设置
星期六, 九月 17th, 2011
Flask是个非常优秀的python microframework,基于Werkzeug、Jinja 2,使用非常方便,性能也非常不错,最近公司创业新项目一直在用。
这里和大家分享2个简单的decorator,分别用于session认证和响应头cache设置
# -*- coding: utf-8 -*-
"""
lib/decorator.py
~~~~~~~~~~~~~~
some decorator defined
:author: linnchord@gmail.com
:date:2011-8-4
"""
from functools import wraps
from flask import session, redirect, url_for, flash, request, make_response
from models.user import User
from util import helper
def login_required(f):
@wraps(f)
def do(*args, **kwargs):
if 'userId' not in session:
#cookie自动登录机制
token=request.cookies.get('auto_login')
if token:
userId, token = token.split('_')
user = User.get_user_by_id(userId)
from matrix import app
if user and helper.md5(str(user.id) + user.password + app.config['SECURIY_KEY'])==token:
session['userId']=user.id
session['phone']=user.phone
session['nickName']=user.nickName
else:
return redirect('/logout')
else:
return redirect('/')
return f(*args, **kwargs)
return do
def no_cache_header(f):
@wraps(f)
def do(*args, **kwargs):
response = make_response(f(*args, **kwargs))
response.headers['pragma'] = 'no-cache'
response.headers['Cache-Control'] = 'no-cache, must-revalidate'
return response
return do
使用很简单方便
@app.route('/user/follow/', methods=['GET'])
@login_required
@no_cache_header
def follow_user(userId,appId):
pass
需要注意的是@no_cache_header修饰应该放在最后,否则逻辑无法正确完成。这个修饰经常用于ajax响应,因为……坑爹的IE总是要缓存第一次请求结果。
这个session auto login机制有个问题,他的auto login cookie始终是不变的,长期使用会有一定安全隐患。更安全的方法是用户每次登录生成一个security key来进行hash,不过这样对于经常换浏览器甚至操作系统的人会造成一定障碍。例如我……经常使用chrome/firefox/ie,还在mac/win下切来切去,某个小说网站每次切了就得重新登录,让我很蛋疼……
Flask SessionStore by redis with msgpack serialized
星期五, 八月 19th, 2011
参考老外代码写了个用于Flask框架的基于redis的session实现,采用msgpack进行序列化。
# -*- coding:utf-8 -*-
"""
lib/RedisSessionStore.py
~~~~~~~~~~~~~~
Flask, session store in redis
ref by https://gist.github.com/994937
:author: linnchord@gmail.com
:date:2011-08-04
"""
import redis
from flask import Flask, request, session, json
from werkzeug.contrib.sessions import Session, SessionStore
import msgpack
class RedisSessionStore(SessionStore):
def __init__(self, key_prefix=None, host='127.0.0.1', port=6379, dbindex=1, expire=1800):
SessionStore.__init__(self)
self.redis = redis.Redis(host,port,dbindex)
self.key_prefix = key_prefix
self.expire = expire
def save(self,session):
key = self._get_session_key(session.sid)
data = msgpack.Packer().pack(dict(session))
#print "set session {0}:{1}".format(key, data)
self.redis.setex(key, data, self.expire)
def delete(self, session):
key = self._get_session_key(session.sid)
self.redis.delete(key)
def get(self, sid):
key = self._get_session_key(sid)
data = self.redis.get(key)
if data is not None:
self.redis.setex(key, data, self.expire)
un = msgpack.Unpacker()
un.feed(data)
data = un.unpack()
else:
data = {}
return self.session_class(data, sid, False)
def _get_session_key(self,sid):
key = self.key_prefix + sid
if isinstance(key, unicode):
key = key.encode('utf-8')
return key
@staticmethod
def init_app(app):
app.session_store = RedisSessionStore(
app.config['SESSION_KEY_PREFIX'],
app.config['SESSION_REDIS_HOST'],
app.config['SESSION_REDIS_PORT'],
app.config['SESSION_REDIS_DB'],
app.config['SESSION_LIFETIME']
)
app.session_key = app.config['SESSION_KEY']
class SessionMixin(object):
__slots__ = ('session_key', 'session_store')
def open_session(self, request):
sid = request.cookies.get(self.session_key, None)
if sid is None:
return self.session_store.new()
else:
return self.session_store.get(sid)
def save_session(self, session, response):
if session.should_save:
self.session_store.save(session)
response.set_cookie(self.session_key, session.sid)
return response
class MyFlask(SessionMixin, Flask): pass
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HOW TO USE
app = MyFlask(__name__)
RedisSessionStore.init_app(app)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
APP CONFIG:
SESSION_REDIS_HOST = '127.0.0.1'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
SESSION_KEY = '_myapp_sid'
SESSION_KEY_PREFIX = 'session_myapp_'
SESSION_LIFETIME = 900