4、Spring Session-HttpSession整合工作原理

来源:互联网 发布:网络工程项目 编辑:程序博客网 时间:2024/06/10 12:13

4.5. HttpSession整合工作原理

幸好HttpSession和HttpServletRequest(获取HttpSession的API)都是接口,这就意味着我们可以为这些API提供自己的实现。

本节介绍Spring Session是如何与HttpSession提供透明的整合。意图就是要让用户理解底层到底发生了什么。这些功能已经整合且您不必在您自己的逻辑层再次实现。

首先我们需要创建一个个性化的HttpServletRequest让它返回一个个性化的HttpSession的实现。看起来就像下面的代码一样:

public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {    public SessionRepositoryRequestWrapper(HttpServletRequest original) {        super(original);    }    public HttpSession getSession() {        return getSession(true);    }    public HttpSession getSession(boolean createNew) {        // 从Spring Session创建一个HttpSession的实现    }    // ... other methods delegate to the original HttpServletRequest ...}

返回HttpSession的所有方法都会被重写。除此之外的所有的其他方法都由HttpServletRequestWrapper实现且只需简单的委托给原始的HttpServletRequest实现。

我们使用被称为SessionRepositoryFilter的Servlet Filter替换了HttpServletRequest的实现。伪代码如下:

public class SessionRepositoryFilter implements Filter {    public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {        HttpServletRequest httpRequest = (HttpServletRequest) request;        SessionRepositoryRequestWrapper customRequest =            new SessionRepositoryRequestWrapper(httpRequest);        chain.doFilter(customRequest, response, chain);    }    // ...}

通过将个性化的HttpServletRequest传入FilterChain,我们就能确保任何被调用的内容都是在使用了我们个性化的HttpSession的Filter之后。这就是我们需要强调的为什么Spring Session的SessionRepositoryFilter必须放在与HttpSession交互的任何内容之前是如此重要。

4.6. 单个浏览器多个HttpSession

Spring Session拥有在单个浏览器实例中支持多个Session的能力。提供这种能力就可以支持在同一个浏览器实例中的多用户认证(如:Google账户)。

Manage Multiple Users Guide提供了一个在同一个浏览器实例中管理多用户的一个可执行样例。你可以根据下面的基础步骤进行集成,但是在您自己的应用中,推荐您遵循详细的 Manage Multiple Users Guide。

让我们来看看Spring Session是如何跟踪多个会话的吧。

4.6.1. 管理单独的Session

Spring Session跟踪HttpSession是通过添加一个名为SESSION的值到cookie中。比如,SESSION的cookie值可能为:

7e8383a4-082c-4ffe-a4bc-c40fd3363c5e

4.6.2. 添加Session

我们可以通过一个包含特殊参数的请求URL添加其他的Session。默认情况下参数名是‘_s’。例如,下面这个URL将会创建一个新的Session:

http://localhost:8080/?_s=1

参数的值并不表示实际的Session Id,很重要的一点是因为我们不会允许客户端自定义Session id,因为要阻止session fixation攻击。此外,我们并不希望session id通过参数的方式传递而被泄露。记住敏感的信息应该在请求头或者请求体中传输。

我们可以利用HttpSessionManager为我们创建URL而不是我们自己去创建。我么可以使用下面的代码从HttpServletRequest中获取HttpSessionManager。

src/main/java/sample/UserAccountsFilter.javaHttpSessionManager sessionManager = (HttpSessionManager) httpRequest        .getAttribute(HttpSessionManager.class.getName());

现在我们就可以使用HttpSessionManager创建一个URL添加其他的session了。

src/main/java/sample/UserAccountsFilter.javaString addAlias = unauthenticatedAlias == null ? sessionManager.getNewSessionAlias(httpRequest) : unauthenticatedAlias; String addAccountUrl = sessionManager.encodeURL(contextPath, addAlias); 
  1. 我们有一个现有的名为unauthenticatedAlias的变量,这个变量的值所指的是一个现有的尚未认证的会话的别名。如果没有会话存在,这个值就为null。这样就可以确保我们所使用现有的尚未认证的会话而不是创建一个新的会话。
  2. 如果所有的会话都已经分配给了用户,我们就会创建一个新的会话别名。
  3. 如果现有的会话没有分配给用户,我们就可以使用它的别名。
  4. 最后,我们创建添加账户URL。该URL包含一个会话别名,这个别名指向一个现有的未认证会话,或者是一个未使用的别名,从而发出创建与该别名相关联的新会话指令。

新会话创建完成之后,SESSION cookie看起来如下:

0 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 1 1d526d4a-c462-45a4-93d9-84a39b6d44ad

解释:

1、有一个id为7e8383a4-082c-4ffe-a4bc-c40fd3363c5e会话。

会话别名是0,比如,如果URL是 http://localhost:8080/?_s=0 ,这个别名也会被创建

这是默认的会话。这就意味着如果没有会话别名被指定,这个会话就会被使用。比如,如果连接是http://localhost:8080/ ,那么默认会话就会被使用。

2、有一个id为1d526d4a-c462-45a4-93d9-84a39b6d44ad的会话

会话别名是1,如果会话别名是1,则使用此会话。例如,如果URL是http://localhost:8080/?_s=1则意味着这个别名将会被使用。

4.6.3. 包含在encodeURL中的自动会话别名

在URL中指定会话别名非常棒的一件事情就是我们可以使用很多个tabs打开不同的已经激活的会话。但是很糟糕的事情呢就是我们必须在我们应用的每个URL中包含会话别名。幸运的是,Spring Session通过下面的这种方式在每个URL中自动包含了会话别名。

HttpServletResponse#encodeURL(java.lang.String)

这就意味着如果你使用标准的标签库的话,会话别名就会自动包含在URL中。例如,如果我们现在在使用的是别名为1的会话,想下面这种情况:

src/main/webapp/index.jsp<c:url value="/link.jsp" var="linkUrl"/><a id="navLink" href="${linkUrl}">Link</a>

将会输出下面的链接:

<a id="navLink" href="/link.jsp?_s=1">Link</a>

原文: https://docs.spring.io/spring-session/docs/2.0.0.M4/reference/html5/#httpsession-how

原创粉丝点击