Jul*_*ian 8 python session customization flask
我正在Flask中编写混合单页web/PhoneGap应用程序.由于PhoneGap应用程序中的cookie基本上不可用,我实现了一个完全避免cookie 的自定义会话接口.它将会话数据存储在应用程序数据库中,并在HTTP请求和响应主体中显式传递会话ID.
我创建了一个带有简化测试用例的GitHub存储库.它本身仍然是一个相当大的项目,但自述文件应该可以帮助您快速找到自己的方式.repo包括七个测试,当使用Flask的默认基于cookie的会话接口时,所有测试都成功,并且所有测试都失败了我的自定义会话接口.主要问题似乎是数据有时不会保留在会话对象上,但这很神秘,因为会话对象继承自Python的内置dict,它不应该自发地忘记数据.此外,与Flask的示例Redis会话片段相比,会话界面简单明了,似乎没有任何明显的错误.
更令人沮丧的是,自定义会话界面似乎在实际应用程序中正常工作.只有单元测试失败.但是,由于这个原因,假设会话接口在所有情况下都能正常工作是不安全的.
将非常感谢帮助.
编辑: Gist不接受缩减的测试用例,因为它包含目录.我现在将它移动到一个完整的GitHub存储库.完成后我会再次更新这篇文章.
新编辑:将简化的测试用例移动到适当的GitHub存储库.自述文件仍然提到"这个要点",对不起.
您的问题主要归结为在测试请求中提供会话令牌.如果您未提供令牌,则会话为空.
我假设您的实际应用程序正确发送会话令牌,因此似乎工作.
修复测试用例以正确传递并不需要太多时间.
在您的会话实现中:
def open_session(self, app, request):
s = Session()
if 't' in request.form:
....
return s
Run Code Online (Sandbox Code Playgroud)
这意味着每个未POST(或PUT)未t发送的请求都将具有空白会话.
而基于cookie的实现将始终具有会话令牌,并且能够加载先前请求的会话.
以下是您的一个示例测试:
def test_authorize_captcha_expired(self):
with self.client as c:
with c.session_transaction() as s:
s['captcha-answer'] = u'one two three'.split()
s['captcha-expires'] = datetime.today() - timedelta(minutes=1)
self.assertEqual(c.post('/test', data={
'ca': 'one two three',
}).status_code, 400)
Run Code Online (Sandbox Code Playgroud)
您尚未t为帖子提供值/test.因此它得到一个没有captcha-expires密钥的空白会话,并且KeyError被引发.
在您的会话实现中:
def save_session(self, app, session, response):
if session.modified and 'token' in session:
...
# save session to database
...
Run Code Online (Sandbox Code Playgroud)
因此当你有:
with c.session_transaction() as s:
s['captcha-answer'] = u'one two three'.split()
s['captcha-expires'] = datetime.today() - timedelta(minutes=1)
Run Code Online (Sandbox Code Playgroud)
实际上没有会话被写入数据库.对于任何后续使用请求.需要注意的是它真的没有需要被写入到数据库中,因为open_session将尝试在每次请求从数据库装载的东西.
要解决大多数情况,您需要在创建会话时提供"令牌",并为使用它的任何请求提供带有该令牌的"t".
因此,我上面使用的样本测试最终会像:
def test_authorize_captcha_expired(self):
with self.client as c:
token = generate_key(SystemRandom())
with c.session_transaction() as s:
s['token'] = token
s['captcha-answer'] = u'one two three'.split()
s['captcha-expires'] = datetime.today() - timedelta(minutes=1)
self.assertEqual(c.post('/test', data={
'ca': 'one two three',
't': token
}).status_code, 400)
Run Code Online (Sandbox Code Playgroud)
...但是当您发出后续请求时,您没有使用新令牌
def test_reply_to_reflection_passthrough(self):
with self.client as c:
token = 'abcdef'
...
response2 = c.post('/reflection/1/reply', data={
'p': 'test4',
'r': 'testmessage',
't': token,
}, ...
Run Code Online (Sandbox Code Playgroud)
通过此处,帖子/reflection/1/reply生成了一个新令牌并保存了它,因此关键键last-reply不在由标识的会话中abcdef.如果这是基于cookie的会话last-reply,则可用于下一个请求.
所以要修复此测试...使用新令牌
def test_reply_to_reflection_passthrough(self):
with self.client as c:
...
response2 = c.post('/reflection/1/reply', data={
...
token = session['token']
with c.session_transaction(method="POST", data={'t':token}) as s:
s['token'] = token
s['last-request'] = datetime.now() - timedelta(milliseconds=1001)
response3 = c.post('/reflection/1/reply', data={
...
Run Code Online (Sandbox Code Playgroud)
在测试中test_bump:
def test_bump(self):
response = self.client.post(
'/admin/tip/action/',
data = {'action': 'Bump', 'rowid': '1',},
follow_redirects=True )
self.assertIn(' tips have been bumped.', response.data)
Run Code Online (Sandbox Code Playgroud)
帖子/admin/tip/action返回重定向.
在这里,您要检查是否存在Flash消息.并且flash消息存储在会话中.
使用基于cookie的会话,会话ID再次与随后的重定向请求一起发送.
由于您的会话ID被指定为post值,因此不会再次发送,会话和flash消息将丢失.
解决此问题的方法不是遵循重定向,而是检查会话以获取由flasks flash方法设置的数据.
def test_bump(self):
with self.client as c:
token = generate_key(SystemRandom())
with c.session_transaction() as s:
s['token'] = token
c.post('/admin/tip/action/',
data={'action': 'Bump', 'rowid': '1', 't': token})
with c.session_transaction(method="POST", data={'t': token}) as s:
self.assertIn(' tips have been bumped.', s['_flashes'][0][1])
Run Code Online (Sandbox Code Playgroud)
我已经发送了一个带有更改的pull请求,如上所述,您会发现测试现在都通过了默认的flask会话和会话实现.
| 归档时间: |
|
| 查看次数: |
675 次 |
| 最近记录: |