让我解释一下这个问题:
我通过Pyramid服务我的静态资产:
config.add_static_view(name='static', path='/var/www/static')
Run Code Online (Sandbox Code Playgroud)
它工作正常.
现在,我有一个自定义会话工厂,可以在数据库中创建会话.它检查浏览器是否提供会话cookie.如果是,它会从数据库中找到一个会话.如果没有,则在DB中创建新会话,并将cookie返回给浏览器.
到现在为止还挺好.
现在,在我的home_view(生成我的主页)内部,我不以任何方式访问请求变量:
@view_config(route_name='home', renderer="package:templates/home.mak")
def home_view(request):
return {}
Run Code Online (Sandbox Code Playgroud)
因此,发生的情况是当用户访问主页时,会话不会在服务器上创建.我认为这是因为金字塔懒洋洋地创建会话- 只有当你访问时request.session.因此,主页请求的响应头不包含任何Set-Cookie会话头.
现在在主页的mako模板中,我正在为JavaScript和CSS文件生成静态URL ...
<link rel="stylesheet" href="${request.static_url(...)}"
<script src="${request.static_url(...)}"></script>
Run Code Online (Sandbox Code Playgroud)
现在,由于我正在为金字塔提供静态资产,所有对这些资产的请求都要通过整个金字塔机制.
所以,当我的浏览器发送获取静态资产的请求时,会发生什么,Pyramid会创建会话.也就是说,Pyramid在数据库中创建会话,并在浏览器发送静态资产请求时发回会话cookie.这是问题#1.
浏览器并行发送静态资产的所有请求.我使用的是最新版本的Firefox和Chrome.由于对实际HTML文档的HTTP请求未返回任何Set-Cookie标头,因此对静态资产的请求没有任何cookie标头.这意味着Pyramid没有看到任何请求的会话cookie,并且它在数据库中创建了一个新的会话,用于获取静态资产的每个请求.
如果我在主页上获取7个静态资源,则会创建7个会话条目.这是因为所有这些请求都与服务器并行,并且没有会话cookie,因此Pyramid会为每个请求创建一个会话.
如果我故意作为主页请求的一部分访问会话,则不会出现此问题.它在数据库中创建一个会话,并向浏览器发送一个cookie,然后浏览器为它从服务器请求的每个静态资产(并行)发送回来.
@view_config(route_name='home', renderer="package:templates/home.mak")
def home_view(request):
if request.session: pass
return {}
Run Code Online (Sandbox Code Playgroud)
我应该如何阻止在静态资产请求上创建会话.更好的是,我希望Pyramid在收到静态资产请求时甚至不会触及会话工厂 - 这可能吗?
其次,我不明白为什么Pyramid正在创建静态请求的新会话?
UPDATE
这是会议工厂.
def DBSessionFactory(
secret,
cookie_name="sess",
cookie_max_age=None,
cookie_path='/',
cookie_domain=None,
cookie_secure=False,
cookie_httponly=False,
cookie_on_exception=True
):
# this is the collable that will be called on every request
# and will be passed the request
def factory(request):
cookieval = request.cookies.get(cookie_name)
session_id = None
session = None
# try getting a possible session id from the cookie
if cookieval is not None:
try:
session_id = signed_deserialize(cookieval, secret)
except ValueError:
pass
# if we found a session id from the cookie
# we try loading the session
if session_id is not None:
# _load_session will return an object that implements
# the partial dict interface (not complete, just the basics)
session = _load_session(session_id)
# if no session id from cookie or no session found
# for the id in the database, create new
if session_id is None or session is None:
session = _create_session()
def set_cookie(response):
exc = getattr(request, 'exception', None)
if exc is not None and cookie_on_exception == False:
return
cookieval = signed_serialize(session.session_id, secret)
response.set_cookie(
cookie_name,
value=cookieval,
max_age = cookie_max_age,
path = cookie_path,
domain = cookie_domain,
secure = cookie_secure,
httponly = cookie_httponly,
)
def delete_cookie(response):
response.delete_cookie(
cookie_name,
path = cookie_path,
domain = cookie_domain,
)
def callback(request, response):
if session.destroyed:
_purge_session(session)
delete_cookie(response)
return
if session.new:
set_cookie(response)
# this updates any changes to the session
_update_session(session)
# at the end of request
request.add_response_callback(callback)
# return the session from a call to the factory
return session
# return from session factory
return factory
Run Code Online (Sandbox Code Playgroud)
然后,
factory = DBSessionFactory('secret')
config.set_session_factory(factory)
Run Code Online (Sandbox Code Playgroud)
UPDATE
我的自定义验证:
class RootFactory:
__acl__ = [
(Allow, Authenticated, 'edit'),
# only allow non authenticated users to login
(Deny, Authenticated, 'login'),
(Allow, Everyone, 'login'),
]
def __init__(self, request):
self.request = request
class SessionAuthenticationPolicy(CallbackAuthenticationPolicy):
def __init__(self, callback=None, debug=False):
self.callback = callback
self.debug = debug
def remember(self, request, principal, **kw):
return []
def forget(self, request):
return []
def unauthenticated_userid(self, request):
if request.session.loggedin:
return request.session.userid
else:
return None
Run Code Online (Sandbox Code Playgroud)
然后,
config.set_root_factory(RootFactory)
config.set_authentication_policy(SessionAuthenticationPolicy())
config.set_authorization_policy(ACLAuthorizationPolicy())
Run Code Online (Sandbox Code Playgroud)
这是一个重现该问题的虚拟项目:
\n\n设置 virtualenv 环境并在其中安装 Pyramid。
安装启动项目:pcreate -s starter IssueApp
删除所有不必要的文件,这样你就有了这个简单的树:
树
\n\n.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CHANGES.txt\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 development.ini\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 issueapp\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 static\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 pyramid.png\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 README.txt\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 setup.py\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,我们将在文件中写入整个应用程序__init__.py- 因此其他所有内容都将被删除。
现在安装项目:(env) $ python setup.py develop这会将您的项目安装到虚拟环境中。
文件development.ini:
[app:main]\nuse = egg:IssueApp#main\n\npyramid.reload_all = true\npyramid.reload_templates = true\npyramid.debug_all = true\npyramid.debug_notfound = true\npyramid.debug_routematch = true\npyramid.prevent_http_cache = true\npyramid.default_locale_name = en\n\n[server:main]\nuse = egg:waitress#main\nhost = 0.0.0.0\nport = 7777\n\n[loggers]\nkeys = root, issueapp\n\n[handlers]\nkeys = console\n\n[formatters]\nkeys = generic\n\n[logger_root]\nlevel = INFO\nhandlers = console\n\n[logger_issueapp]\nlevel = INFO\nhandlers =\nqualname = issueapp\n\n[handler_console]\nclass = StreamHandler\nargs = (sys.stderr,)\nlevel = NOTSET\nformatter = generic\n\n[formatter_generic]\nformat = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s\nRun Code Online (Sandbox Code Playgroud)\n\n文件__init__.py:
from pyramid.config import Configurator\n\nfrom pyramid.view import view_config\nfrom pyramid.response import Response\n\nfrom pyramid.authentication import CallbackAuthenticationPolicy\nfrom pyramid.authorization import ACLAuthorizationPolicy\n\nfrom pyramid.security import (\n Allow, Deny,\n Everyone, Authenticated,\n)\n\n\ndef main(global_config, **settings):\n """ This function returns a Pyramid WSGI application.\n """\n config = Configurator(settings=settings)\n\n #config.add_static_view(\'static\', \'static\', cache_max_age=3600)\n config.add_static_view(name=\'static\', path=\'issueapp:static\')\n config.add_route(\'home\', \'/\')\n\n config.set_root_factory(RootFactory)\n config.set_authentication_policy(DummyAuthPolicy())\n config.set_authorization_policy(ACLAuthorizationPolicy())\n\n config.scan()\n return config.make_wsgi_app()\n\n\n@view_config(route_name=\'home\')\ndef home_view(request):\n src = request.static_url(\'issueapp:static/pyramid.png\')\n return Response(\'<img src=\'+ src + \'>\')\n\n\nclass RootFactory:\n __acl__ = [\n (Allow, Authenticated, \'edit\'),\n (Deny, Authenticated, \'login\'),\n (Allow, Everyone, \'login\'),\n ]\n\n def __init__(self, request):\n self.request = request\n\n\nclass DummyAuthPolicy(CallbackAuthenticationPolicy):\n def __init__(self, callback=None, debug=False):\n self.callback = callback\n self.debug = debug\n\n def remember(self, request, principal, **kw):\n return []\n\n def forget(self, request):\n return []\n\n def unauthenticated_userid(self, request):\n # this will print the request url\n # so we can know which request is causing auth code to be called \n print(\'[auth]: \' + request.url)\n\n # this means the user is authenticated\n return "user"\nRun Code Online (Sandbox Code Playgroud)\n\n现在运行应用程序
\n\npserve development.ini --reload\nStarting subprocess with file monitor\nStarting server in PID 2303.\nserving on http://0.0.0.0:7777\nRun Code Online (Sandbox Code Playgroud)\n\n最后,清除浏览器中的所有历史记录(这很重要,否则问题可能不会自行暴露)并访问该页面。这会打印在控制台上:
\n\n[auth]: http://192.168.56.102:7777/static/pyramid.png \nRun Code Online (Sandbox Code Playgroud)\n\n这表明正在为静态请求调用身份验证代码。
\n\n现在,当我将日志级别设置为 时DEBUG,这是访问页面时控制台的输出:
\npservedevelopment.ini --reload\n使用文件监视器启动子进程\n以 PID 2339 启动服务器。\n在 http://0.0.0.0:7777\n2013-03-27 03:40:55,539 DEBUG [issueapp][Dummy -2] 与 url http://192.168.56.102:7777/ 匹配的路由;路由名称:\'home\',路径信息:\'/\',模式:\'/\',matchdict:{},谓词:\'\'\n2013-03-27 03:40:55,540 DEBUG [issueapp] [Dummy-2] url http://192.168.56.102:7777/ 的 debug_authorization(根据上下文查看名称 \'\'):允许(未注册权限)\n2013-03-27 03:40:55,685 DEBUG [issueapp] [Dummy-3] 与 url http://192.168.56.102:7777/static/pyramid.png 匹配的路由;路由名称:\'__static/\',路径信息:\'/static/pyramid.png\',模式:\'static/*subpath\',matchdict:{\'subpath\':(\'pyramid.png\' ,)},谓词:\'\'\n[auth]:http://192.168.56.102:7777/static/pyramid.png\n2013-03-27 03:40:55,687 DEBUG [issueapp][Dummy-3 ] url http://192.168.56.102:7777/static/pyramid.png 的 debug_authorization(根据上下文查看名称 \'\' ):ACLDenied 权限 \'__no_permission_required__\' via ACE \'\' in ACL [(\'Allow \', \'system.Authenticated\', \'编辑\'), (\'拒绝\', \'system.Authenticated\', \'登录\'), (\'允许\', \'系统.Everyone\', \'login\')] 主体上下文 [\'system.Everyone\', \'system.Authenticated\', \'user\']\n\n\n\n请注意,[auth]: ...消息仅打印一次 - 对于静态资产请求,而不是主页请求。这很奇怪,因为这意味着针对静态资产而不是针对正常请求咨询身份验证策略。(当然,除非涉及许可,但在我看来不是)。
| 归档时间: |
|
| 查看次数: |
2120 次 |
| 最近记录: |