使用 spring-session 获取当前会话
Get the current session with spring-session
这是我的问题:我正在编写一个平台,我将提供给客户来实施他们的项目。因此,在我的平台中,我创建了一个 SessionService
,其中包含 getCurrentSession
、getAttribute
、setAttribute
等方法。在 spring-session 之前,我的 getCurrentMethod
看起来像这样:
@Override
public HttpSession getCurrentSession() {
if (this.session == null) {
final ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true); // true == allow create
}
return this.session;
}
虽然它看起来很丑而且没有像 redis 那样的支持,但它工作得很好。现在我想迁移到 spring-session
并且我希望使用 SessionRepository
来查找用户的当前会话,但是我只能在其中看到 getSession(String id)
。我相信 id 存储在 cookie 中,因此要使用它,我可能必须将 HttpServletRequest
对象从我的控制器传递到我的外观,再传递到非常靠近数据库层的服务层。这对我来说似乎是一个非常糟糕的主意,所以我的问题是:有没有办法让 currentSession 靠近 db 层?我认为的一种方法是编写一个将调用控制器的拦截器,它将在存储库或服务中设置当前会话?我只是不确定这是正确的方法。
从服务层获取Session Id
您可以使用 RequestContextHolder
检索会话 ID、设置属性和删除属性。
RequestContextHolder
通常使用 RequestContextListener
或 RequestContextFilter
设置。 Spring 会话不适用于 RequestContextListener
,因为 Spring 会话无法在调用 RequestContextListener
之前包装请求。
不幸的是,这意味着对于 Spring 引导应用程序,RequestContextHolder
无法开箱即用。要解决这个问题,您可以创建一个 RequestContextFilter
Bean。有关此问题的更新,请参阅 spring-boot/gh-2637。
我应该把它放在会话中吗?
仅仅因为很容易将很多对象放在会话中并且存储在 Redis 中并不意味着这样做是正确的。
请记住,每次请求都会检索整个会话。因此,尽管 Redis 速度很快,但如果会话中有很多对象,这可能会产生重大影响。显然可以针对您的情况优化实现,但我认为会话的概念通常持有这种属性。
一般的经验法则是,"Do I need this object for over 95% of my requests?"(将此视为我的几乎所有请求)。如果是这样,它可能是会话的候选者。在大多数情况下,如果对象符合此条件,则它应该是安全相关的。
是否应该从服务层的ThreadLocal获取session id?
这当然是有争议的,因为代码既是一门科学,也是一门艺术。
但是,我认为您不应该在整个体系结构中从线程区域设置变量中获取会话 ID。这样做感觉有点像从ThreadLocale中的HttpServletRequest获取一个"Person id"和获取当前的"Person id"。相反,应该从控制器获取值并将其传递到您的服务层。
这是我的问题:我正在编写一个平台,我将提供给客户来实施他们的项目。因此,在我的平台中,我创建了一个 SessionService
,其中包含 getCurrentSession
、getAttribute
、setAttribute
等方法。在 spring-session 之前,我的 getCurrentMethod
看起来像这样:
@Override
public HttpSession getCurrentSession() {
if (this.session == null) {
final ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true); // true == allow create
}
return this.session;
}
虽然它看起来很丑而且没有像 redis 那样的支持,但它工作得很好。现在我想迁移到 spring-session
并且我希望使用 SessionRepository
来查找用户的当前会话,但是我只能在其中看到 getSession(String id)
。我相信 id 存储在 cookie 中,因此要使用它,我可能必须将 HttpServletRequest
对象从我的控制器传递到我的外观,再传递到非常靠近数据库层的服务层。这对我来说似乎是一个非常糟糕的主意,所以我的问题是:有没有办法让 currentSession 靠近 db 层?我认为的一种方法是编写一个将调用控制器的拦截器,它将在存储库或服务中设置当前会话?我只是不确定这是正确的方法。
从服务层获取Session Id
您可以使用 RequestContextHolder
检索会话 ID、设置属性和删除属性。
RequestContextHolder
通常使用 RequestContextListener
或 RequestContextFilter
设置。 Spring 会话不适用于 RequestContextListener
,因为 Spring 会话无法在调用 RequestContextListener
之前包装请求。
不幸的是,这意味着对于 Spring 引导应用程序,RequestContextHolder
无法开箱即用。要解决这个问题,您可以创建一个 RequestContextFilter
Bean。有关此问题的更新,请参阅 spring-boot/gh-2637。
我应该把它放在会话中吗?
仅仅因为很容易将很多对象放在会话中并且存储在 Redis 中并不意味着这样做是正确的。
请记住,每次请求都会检索整个会话。因此,尽管 Redis 速度很快,但如果会话中有很多对象,这可能会产生重大影响。显然可以针对您的情况优化实现,但我认为会话的概念通常持有这种属性。
一般的经验法则是,"Do I need this object for over 95% of my requests?"(将此视为我的几乎所有请求)。如果是这样,它可能是会话的候选者。在大多数情况下,如果对象符合此条件,则它应该是安全相关的。
是否应该从服务层的ThreadLocal获取session id?
这当然是有争议的,因为代码既是一门科学,也是一门艺术。
但是,我认为您不应该在整个体系结构中从线程区域设置变量中获取会话 ID。这样做感觉有点像从ThreadLocale中的HttpServletRequest获取一个"Person id"和获取当前的"Person id"。相反,应该从控制器获取值并将其传递到您的服务层。