让我解释一下问题:
我通过 Pyramid 为我的静态资产提供服务:
config.add_static_view(name='static', path='/var/www/static')
它工作正常。
现在,我有一个在数据库中创建会话的自定义会话工厂。它检查浏览器是否提供会话 cookie。如果是这样,它会从数据库中找到一个会话。如果没有,则在 DB 中创建一个新会话,并将一个 cookie 返回给浏览器。
到目前为止,一切都很好。
现在,在我的home_view
(生成我的主页)内部,我不会以任何方式访问请求变量:
@view_config(route_name='home', renderer="package:templates/home.mak")
def home_view(request):
return {}
因此,当用户访问主页时,不会在服务器上创建会话。我认为这是因为 Pyramid 懒惰地创建会话-只有当您访问时request.session
。因此,主页请求的响应标头不包含任何Set-Cookie
会话标头。
现在在主页的 mako 模板中,我正在为 JavaScript 和 CSS 文件生成静态 URL...
<link rel="stylesheet" href="${request.static_url(...)}"
<script src="${request.static_url(...)}"></script>
现在,由于我从 Pyramid 提供静态资产,因此对这些资产的所有请求都通过整个 Pyramid 机器。
因此,当我的浏览器发送获取静态资产的请求时,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 {}
我应该如何防止在静态资产请求上创建会话。更好的是,我希望 Pyramid 在收到静态资产请求时甚至不接触会话工厂——这可能吗?
其次,我不明白为什么 Pyramid 在静态请求上创建一个新会话?
更新
这是会话工厂。
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
接着,
factory = DBSessionFactory('secret')
config.set_session_factory(factory)
更新
我的自定义身份验证:
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
接着,
config.set_root_factory(RootFactory)
config.set_authentication_policy(SessionAuthenticationPolicy())
config.set_authorization_policy(ACLAuthorizationPolicy())