摘要: session通用策略 Session在浏览器通常是通过cookie保存的,cookie里保存了jessionid,代表用户的session id。一个访问路径只有一个session cookie(事实上在客户端就只有一个cookie,jsessionid是作为cookie值的一部分,这里把cookie抽象成类似服务器端的实现),也就是一个访问路径在一个浏览器上只有一个se
session通用策略
Session在浏览器通常是通过cookie保存的,cookie里保存了jessionid,代表用户的session id。一个访问路径只有一个session cookie(事实上在客户端就只有一个cookie,jsessionid是作为cookie值的一部分,这里把cookie抽象成类似服务器端的实现),也就是一个访问路径在一个浏览器上只有一个session,这是绝大多数容器对session的实现。而spring却可以支持单浏览器多用户session。下面就看看spring是怎样去支持多用户session的。
对多用户session的支持
spring session通过增加session alias概念来实现多用户session,每一个用户都映射成一个session alias。当有多个session时,spring会生成“alias1 sessionid1 alias2 sessid2…….”这样的cookie值结构。
spring session提交时如果有新session生成,会触发onNewSession动作生成新的session cookie
public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
Set<String> sessionIdsWritten = getSessionIdsWritten(request);
if(sessionIdsWritten.contains(session.getId())) {
return;
}
sessionIdsWritten.add(session.getId());
Map<String,String> sessionIds = getSessionIds(request);
String sessionAlias = getCurrentSessionAlias(request);
sessionIds.put(sessionAlias, session.getId());
Cookie sessionCookie = createSessionCookie(request, sessionIds);
response.addCookie(sessionCookie);
}
a) 确保已经存在cookie里的session不会再被处理。
b) 生成一个包含所有alias的session id的map,并通过这个map构造新的session cookie值。
createSessionCookie会根据一个alias-sessionid的map去构造session cookie。
private Cookie createSessionCookie(HttpServletRequest request,
Map<String, String> sessionIds) {
//cookieName是"SESSION",spring的session cookie都是
//以"SESSION"命名的
Cookie sessionCookie = new Cookie(cookieName,"");
//省略部分非关键逻辑
if(sessionIds.isEmpty()) {
sessionCookie.setMaxAge(0);
return sessionCookie;
}
if(sessionIds.size() == 1) {
String cookieValue = sessionIds.values().iterator().next();
sessionCookie.setValue(cookieValue);
return sessionCookie;
}
StringBuffer buffer = new StringBuffer();
for(Map.Entry<String,String> entry : sessionIds.entrySet()) {
String alias = entry.getKey();
String id = entry.getValue();
buffer.append(alias);
buffer.append(" ");
buffer.append(id);
buffer.append(" ");
}
buffer.deleteCharAt(buffer.length()-1);
sessionCookie.setValue(buffer.toString());
return sessionCookie;
}
a) 当session被invalidate,可能会存在seesionids为空的情况,这种情况下将session cookie的最大失效时间设成立即。
b) 如果只有一个session id,则和普通session cookie一样处理,cookie值就是session id。
c) 如果存在多个session id,则生成前文提到的session cookie值结构。
session cookie的获取
getSessionIds方法会取出request里的session cookie值,并且对每种可能的值结构进行相应的格式化生成一个key-value的map。
public Map<String,String> getSessionIds(HttpServletRequest request) {
Cookie session = getCookie(request, cookieName);
String sessionCookieValue = session == null ? "" : session.getValue();
Map<String,String> result = new LinkedHashMap<String,String>();
StringTokenizer tokens = new StringTokenizer(sessionCookieValue, " ");
//单用户cookie的情况
if(tokens.countTokens() == 1) {
result.put(DEFAULT_ALIAS, tokens.nextToken());
return result;
}
while(tokens.hasMoreTokens()) {
String alias = tokens.nextToken();
if(!tokens.hasMoreTokens()) {
break;
}
String id = tokens.nextToken();
result.put(alias, id);
}
return result;
}
- 对单用户session cookie的处理,只取出值,默认为是默认别名(默认为0)用户的session。
- 对多用户,则依据值结构的格式生成alias-sessionid的map。
- 以上两种格式化都是对创建session的逆操作。
getCurrentSessionAlias用来获取当前操作用户。可以通过在request里附加alias信息,从而让spring可以判断是哪个用户在操作。别名是通过”alias name=alias”这样的格式传入的,alias name默认是_s,可以通过setSessionAliasParamName(String)方法修改。我们可以在url上或者表单里添加”_s=your user alias”这样的形式来指明操作用户的别名。如果不指明用户别名,则会认为是默认用户,可以通过setSessionAliasParamName(null)取消别名功能。
public String getCurrentSessionAlias(HttpServletRequest request) {
if(sessionParam == null) {
return DEFAULT_ALIAS;
}
String u = request.getParameter(sessionParam);
if(u == null) {
return DEFAULT_ALIAS;
}
if(!ALIAS_PATTERN.matcher(u).matches()) {
return DEFAULT_ALIAS;
}
return u;
}
触发session提交
spring会通过两个方面确保session提交:
a) response提交,主要包括response的sendRedirect和sendError以及其关联的字节字符流的flush和close方法。
abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
public OnCommittedResponseWrapper(HttpServletResponse response) {
super(response);
}
/**
* Implement the logic for handling the {@link javax.servlet.http.HttpServletResponse} being committed
*/
protected abstract void onResponseCommitted();
@Override
public final void sendError(int sc) throws IOException {
doOnResponseCommitted();
super.sendError(sc);
}
//sendRedirect处理类似sendError
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new SaveContextServletOutputStream(super.getOutputStream());
}
@Override
public PrintWriter getWriter() throws IOException {
return new SaveContextPrintWriter(super.getWriter());
}
private void doOnResponseCommitted() {
if(!disableOnCommitted) {
onResponseCommitted();
disableOnResponseCommitted();
} else if(logger.isDebugEnabled()){
logger.debug("Skip invoking on");
}
}
private class SaveContextPrintWriter extends PrintWriter {
private final PrintWriter delegate;
public SaveContextPrintWriter(PrintWriter delegate) {
super(delegate);
this.delegate = delegate;
}
public void flush() {
doOnResponseCommitted();
delegate.flush();
}
//close方法与flush方法类似
}
//SaveContextServletOutputStream处理同字符流
}
onResponseCommitted的实现由子类SessionRepositoryResponseWrapper提供
private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper {
private final SessionRepositoryRequestWrapper request;
/**
* @param response the response to be wrapped
*/
public SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) {
super(response);
if(request == null) {
throw new IllegalArgumentException("request cannot be null");
}
this.request = request;
}
@Override
protected void onResponseCommitted() {
request.commitSession();
}
}
response提交后触发了session提交。
b) SessionRespositoryFilter
仅仅通过response提交时触发session提交并不能完全保证session的提交,有些情况下不会触发response提交,比如对相应资源的访问没有servlet处理,这种情况就需要通过全局filter做保证。
protectedvoiddoFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
//省略//filterChain会在所有filter都执行完毕后调用对应的servlet
filterChain.doFilter(strategyRequest, strategyResponse);
} finally {
//所有的处理都完成后提交session
wrappedRequest.commitSession()
}
https://yq.aliyun.com/articles/57421
相关推荐
赠送jar包:spring-session-data-redis-2.0.4.RELEASE.jar; 赠送原API文档:spring-session-data-redis-2.0.4.RELEASE-javadoc.jar; 赠送源代码:spring-session-data-redis-2.0.4.RELEASE-sources.jar; 赠送...
spring-session-1.2.1 源码spring-session-1.2.1 源码spring-session-1.2.1 源码
1. **配置Spring-Session**:在Spring Boot项目中,添加spring-session和spring-session-data-redis依赖,并在配置文件中设置Redis的相关连接信息。 2. **启用Spring-Session**:在Spring Boot的主配置类上添加@...
通过阅读和理解Spring-ORM的源码,我们可以深入理解Spring如何与ORM(对象关系映射)框架进行交互,以及Spring如何简化数据访问层的开发。 1. **IoC容器与数据源** Spring的核心是IoC(Inversion of Control)容器...
Spring-Session 实现原理源码分析 Spring-Session 是 Spring旗下的一个项目,旨在解决 Session 管理问题。它可以轻松快捷地集成到我们的应用程序中,并提供了多种存储 Session 的方式。下面是 Spring-Session 的...
Spring支持五种Bean的作用域:单例(Singleton)、原型(Prototype)、请求(Request)、session(Session)和全局会话(Global Session)。不同的作用域决定了Bean的实例化策略和生命周期。 7. **Bean后处理器** ...
在session共享中遇到的坑。自己通过更改源码实现自定义功能
1. **添加依赖**:在`pom.xml`中,你需要引入Spring Session和Redis的相关依赖,如`spring-boot-starter-data-redis`和`spring-session-data-redis`。 2. **配置Redis**:在Spring Boot的配置文件`application....
Spring MVC、Spring 和 Hibernate 是Java开发中常用的三大框架,它们分别负责不同的职责:Spring MVC用于构建Web应用程序的前端控制器,Spring 提供了全面的依赖注入(DI)和面向切面编程(AOP)功能,而Hibernate则...
- **Session Management**:管理用户会话,防止会话固定攻击和并发会话控制。 2. **认证流程**: 认证流程在Spring Security中分为以下步骤: - 用户提交登录信息。 - `AuthenticationManager`验证用户凭证。 ...
在这个源码分析中,我们将深入探讨`spring-security-web 3.1.2`和`spring-security-oauth2`这两个关键组件。 首先,`spring-security-web`是Spring Security的核心库,它提供了Web应用程序的基本安全功能,如HTTP...
本文将深入探讨Spring-MyBatis 2.0.0版本的整合原理,以及如何通过`org.apache.ibatis.session.SqlSessionFactory`进行交互。 一、Spring与MyBatis整合概述 Spring与MyBatis的整合主要体现在Spring的IoC...
在这个源码中,我们可以深入理解Spring如何处理事务的生命周期、回滚规则以及与各种数据源的集成。 首先,`spring-tx`模块主要包含以下几个关键组件: 1. **PlatformTransactionManager**: 这是Spring事务管理的...
2. **添加依赖**:在Tomcat的`lib`目录下,添加与Redis相关的Java库,例如`spring-session-data-redis`或`jedis`。压缩包中的jar文件可能包含了这些库。 3. **配置Tomcat**:在`$CATALINA_HOME/conf/context.xml`或...
本示例源码主要探讨了如何利用Session技术来实现一个简单的购物车功能。Session是Web应用程序中用于跟踪用户状态的一种机制,它能够在用户的不同请求之间保存数据,使得在用户浏览网页时能够保留他们的选择,比如...
《深入解析Spring JMS源码》 Spring JMS(Java Message Service)是Spring框架的一部分,它为Java消息传递提供了一种轻量级的抽象层,使得开发者能够方便地在应用程序中使用JMS。Spring JMS提供了对JMS API的高度...
标题中的"redis-session-manager-redis-session-manager-2.0.2.zip"表明这是一个针对Redis Session Manager的2.0.2版本的软件包,通常包含了该版本的源码、编译好的二进制文件或者相关配置文件。 描述中提到"redis-...
它提供了Session接口,用于管理事务和对象的状态,同时支持HQL(Hibernate Query Language),这是对SQL的一种面向对象的扩展。 Spring框架则是一个全面的后端解决方案,它不仅提供了IoC(Inversion of Control,...
《Spring Security 3.1.3 源码实例解析》 Spring Security 是一个功能强大的安全框架,广泛应用于Java EE应用程序的安全控制。本实例聚焦于Spring Security 3.1.3版本,为初学者提供了宝贵的参考资料,帮助开发者...
这个压缩包文件“redis-session-manager-redis-session-manager-2.0.2.tar.gz”是该工具的2.0.2版本,适用于Linux操作系统。这个版本可能是稳定版,提供免费下载,方便开发者在不同的项目环境中使用。 Redis是一种...