使用springboot+springsession实现分布式session以及源码解析

1
接上问springboot使用redis

springsession是什么

实现分布式session管理

为什么要使用springsession

spring全家桶,不想自己实现分布式session管理可以使用

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>

添加配置

1
2
3
4
@EnableRedisHttpSession
public class HttpSessionConfiguration {
}

测试

image
image

原理分析

SpringHttpSession

  • 第一步查看@EnableRedisHttpSession

    1
    2
    3
    4
    5
    6
    7
    8
    @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
    @Target({ java.lang.annotation.ElementType.TYPE })
    @Documented
    @Import(RedisHttpSessionConfiguration.class)
    @Configuration
    public @interface EnableRedisHttpSession {
    ...
    }
  • 进二步查看RedisHttpSessionConfiguration

    1
    2
    3
    4
    5
    6
    @Configuration
    @EnableScheduling
    public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
    implements EmbeddedValueResolverAware, ImportAware {
    ...
    }
  • 第三步发现其集成自SpringHttpSessionConfiguration查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class SpringHttpSessionConfiguration implements ApplicationContextAware {
private CookieHttpSessionStrategy defaultHttpSessionStrategy = new CookieHttpSessionStrategy();
private boolean usesSpringSessionRememberMeServices;
private ServletContext servletContext;
private CookieSerializer cookieSerializer;
private HttpSessionStrategy httpSessionStrategy = this.defaultHttpSessionStrategy;
private List<HttpSessionListener> httpSessionListeners = new ArrayList<HttpSessionListener>();
@PostConstruct
public void init() {
if (this.cookieSerializer != null) {
this.defaultHttpSessionStrategy.setCookieSerializer(this.cookieSerializer);
}
else if (this.usesSpringSessionRememberMeServices) {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setRememberMeRequestAttribute(
SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
this.defaultHttpSessionStrategy.setCookieSerializer(cookieSerializer);
}
}
@Bean
public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
return new SessionEventHttpSessionListenerAdapter(this.httpSessionListeners);
}
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(
sessionRepository);
sessionRepositoryFilter.setServletContext(this.servletContext);
if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
sessionRepositoryFilter.setHttpSessionStrategy(
(MultiHttpSessionStrategy) this.httpSessionStrategy);
}
else {
sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
}
return sessionRepositoryFilter;
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
if (ClassUtils.isPresent(
"org.springframework.security.web.authentication.RememberMeServices",
null)) {
this.usesSpringSessionRememberMeServices = !ObjectUtils
.isEmpty(applicationContext
.getBeanNamesForType(SpringSessionRememberMeServices.class));
}
}
@Autowired(required = false)
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Autowired(required = false)
public void setCookieSerializer(CookieSerializer cookieSerializer) {
this.cookieSerializer = cookieSerializer;
}
@Autowired(required = false)
public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
this.httpSessionStrategy = httpSessionStrategy;
}
@Autowired(required = false)
public void setHttpSessionListeners(List<HttpSessionListener> listeners) {
this.httpSessionListeners = listeners;
}
}
发现其session默认的策略是使用
defaultHttpSessionStrategy=new CookieHttpSessionStrategy();cookie来实现
继续看
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(
sessionRepository);
sessionRepositoryFilter.setServletContext(this.servletContext);
if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
sessionRepositoryFilter.setHttpSessionStrategy(
(MultiHttpSessionStrategy) this.httpSessionStrategy);
}
else {
sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
}
return sessionRepositoryFilter;
}
传入参数SessionRepository的实现类RedisOperationsSessionRepository在RedisHttpSessionConfiguration被进行创建所以sessionRepository使用的就是RedisOperationsSessionRepository用来做于存储
  • 继续查看SessionRepositoryFilter
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    public class SessionRepositoryFilter<S extends ExpiringSession>
    extends OncePerRequestFilter {
    ...
    }
    继承自OncePerRequestFilter
    abstract class OncePerRequestFilter implements Filter {
    public final void doFilter(ServletRequest request, ServletResponse response,
    FilterChain filterChain) throws ServletException, IOException {
    调用doFilterInternal由SessionRepositoryFilter实现
    @Override
    protected void doFilterInternal(HttpServletRequest request,
    HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {
    request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
    SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
    request, response, this.servletContext);
    SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
    wrappedRequest, response);
    HttpServletRequest strategyRequest = this.httpSessionStrategy
    .wrapRequest(wrappedRequest, wrappedResponse);
    HttpServletResponse strategyResponse = this.httpSessionStrategy
    .wrapResponse(wrappedRequest, wrappedResponse);
    try {
    filterChain.doFilter(strategyRequest, strategyResponse);
    }
    finally {
    wrappedRequest.commitSession();
    }
    }
    包装请求,响应对象
    根据策略处理包装请求对象
    最后wrappedRequest.commitSession();
    HttpSessionWrapper wrappedSession = getCurrentSession();
    if (wrappedSession == null) {
    if (isInvalidateClientSession()) {
    SessionRepositoryFilter.this.httpSessionStrategy
    .onInvalidateSession(this, this.response);
    }
    }
    else {
    S session = wrappedSession.getSession();
    SessionRepositoryFilter.this.sessionRepository.save(session);
    if (!isRequestedSessionIdValid()
    || !session.getId().equals(getRequestedSessionId())) {
    SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,
    this, this.response);
    }
    }
    这就是最终处理,就不做详细解释了

几张图

image

SessionRepository的实现类
image

image

参考

springsession