spring-session是spring旗下的一个项目,把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题。可简单快速且无缝的集成到我们的应用中。
SpringSession核心原理 - 装饰者模式
分析@EnableRedisHttpSession,
@Import({RedisHttpSessionConfiguration.class})
@Configuration( proxyBeanMethods = false)
public @interface EnableRedisHttpSession {
public class RedisHttpSessionConfiguration
extends SpringHttpSessionConfiguration // 继承
implements 。。。{
// 后面SessionRepositoryFilter会构造时候自动注入他
@Bean // 操作session的方法,如getSession() deleteById()
public RedisIndexedSessionRepository sessionRepository() {
SessionRepositoryFilter,每个请求都要经过该filter
public class SpringHttpSessionConfiguration
implements ApplicationContextAware {
@Bean
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) { // 注入前面的bean
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
return sessionRepositoryFilter;
}
前面我们@Bean注入了sessionRepositoryFilter,他是一个过滤器,那我们需要知道他过滤做了什么事情:
- 原生的获取session时是通过HttpServletRequest获取的
- 这里对request进行包装,并且重写了包装request的getSession()方法
@Override // SessionRepositoryFilter.java
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
//对原生的request、response进行包装
// SessionRepositoryRequestWrapper.getSession()
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
request, response, this.servletContext);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
wrappedRequest, response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession();
}
}
spring-session无缝替换应用服务器的request大概原理是:
1.自定义个Filter,实现doFilter方法
2.继承 HttpServletRequestWrapper 、HttpServletResponseWrapper 类,重写getSession等相关方法(在这些方法里调用相关的 session存储容器操作类)。
3.在 第一步的doFilter中,new 第二步 自定义的request和response的类。并把它们分别传递 到 过滤器链
4.把该filter配置到 过滤器链的第一个位置上
使用
搭建环境
导入依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
修改配置,指定redis
spring.session.store-type=redis
server.servlet.session.timeout=30m
spring.redis.host=192.168.56.10
为springboot启动类添加注解
@EnableRedisHttpSession //创建了一个springSessionRepositoryFilter ,负责将原生HttpSession 替换为Spring Session的实现
public class GulimallAuthServerApplication {
扩大session作用域
-
由于默认使用jdk进行序列化,通过导入RedisSerializer修改为json序列化
-
并且通过修改CookieSerializer扩大session的作用域至**.gulimall.com
@Configuration
public class GulimallSessionConfig {
@Bean // redis的json序列化
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
@Bean // cookie
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("GULISESSIONID"); // cookie的键
serializer.setDomainName("gulimall.com"); // 扩大session作用域,也就是cookie的有效域
return serializer;
}
}
把这个配置放到每个微服务下
session的保存
@GetMapping({"/login.html","/","/index","/index.html"}) // auth
public String loginPage(HttpSession session){
// 从会话从获取loginUser
Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);// "loginUser";
System.out.println("attribute:"+attribute);
if(attribute == null){
return "login";
}
System.out.println("已登陆过,重定向到首页");
return "redirect:http://gulimall.com";
}
@PostMapping("/login") // auth
public String login(UserLoginVo userLoginVo,
RedirectAttributes redirectAttributes,
HttpSession session){
// 远程登录
R r = memberFeignService.login(userLoginVo);
if(r.getCode() == 0){
// 登录成功
MemberRespVo respVo = r.getData("data", new TypeReference<MemberRespVo>() {});
// 放入session // key为loginUser
session.setAttribute(AuthServerConstant.LOGIN_USER, respVo);//loginUser
log.info("\n欢迎 [" + respVo.getUsername() + "] 登录");
// 登录成功重定向到首页
return "redirect:http://gulimall.com";
}else {
HashMap<String, String> error = new HashMap<>();
// 获取错误信息
error.put("msg", r.getData("msg",new TypeReference<String>(){}));
redirectAttributes.addFlashAttribute("errors", error);
return "redirect:http://auth.gulimall.com/login.html";
}
}