这期内容当中小编将会给大家带来有关springboot中如何整合spring-session,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
它可以替代 HttpSesession
。而且改动极小,对应用透明。底层可以使用内存,Redis等存储Session信息。通过Redis
这种方式可以做到Session
共享,在集群环境中所有节点共享Session。
文档 https://docs.spring.io/spring-session/docs/current/reference/html5/
使用 spring-session-data-redis,一定要先整合redis到项目。
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置类:
RedisSessionProperties
spring:
session:
timeout: 1800 # session的过期时间,单位是秒
store-type: REDIS # session的存储类型,枚举
redis:
namespace: "spring:session" # session存储在redis中的命名空间
flush-mode: IMMEDIATE # 刷出模式,枚举:ON_SAVE ,IMMEDIATE
cleanup-cron: "0 * * * * *" # 定时清理过期session任务的`cron`表达式
关于 spring.session.redis.flush-mode
ON_SAVE
只有当
SessionRepository.save(Session)
方法被调用时, 才会将session中的数据同步到redis中. 在web 应用中, 当请求完成响应后, 才开始同步. 也就是说在执行response 之前session数据都是缓存在本地的。
IMMEDIATE
当执行
SessionRepository.createSession()
时, 会将session数据同步到redis中; 当对session的attribute进行set/remove 等操作时, 也会同步session中的数据到redis中。它是实时同步的
使用并需要修改什么,像平时一样。获取到 Servlet的Session就是了。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@RequestMapping("/test")
@Controller
public class TestController {
@GetMapping("/session")
public ModelAndView session(HttpServletRequest request) {
HttpSession httpSession = request.getSession();
// 是否新创建的 true
System.out.println(httpSession.isNew());
// 实现类 org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper
System.out.println(httpSession.getClass().getName());
// 写入数据到session(底层使用redis存储)
httpSession.setAttribute("name", "SpringBoot中文社区");
return new ModelAndView("test/test");
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
Hello ${name}
</body>
</html>
默认情况下,客户端使用Cookie来存储会话id
Connection: keep-alive
Content-Encoding: gzip
Content-Language: zh-CN
Content-Type: text/html;charset=UTF-8
Date: Thu, 17 Oct 2019 08:57:07 GMT
Server: nginx
Set-Cookie: PHPSESSIONID=Y2YwMDM1YjctMjBiYy00OWRiLWI5NGItZjFmNDU4ZDcxNThm; Max-Age=36000; Expires=Thu, 17 Oct 2019 18:57:07 GMT; Path=/; HttpOnly; SameSite=Lax
Transfer-Encoding: chunked
server:
servlet:
session:
cookie:
name: PHPSESSIONID #cookie名称
domain: # 域
path: # 路径
comment: # 备注
httpOnly: # 是否仅用于http传输
secure: # 是否仅在SSL的情况下传输
maxAge: # 生命周期
自定义
CookieSerializer
到IOC。
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
通俗的理解就是,可以建立key和session的索引,根据某些key获取到session。
需求:根据用户id,获取到它的会话
value仅仅接受字符串
@Autowired
FindByIndexNameSessionRepository<? extends Session> sessions;
.... 代码省略
Integer userId = user.getId();
// 往session中存入用户的id信息
request.getSession().setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, userId + "");
@Autowired
FindByIndexNameSessionRepository<? extends Session> sessions;
.... 代码省略
Integer userId = user.getId();
// 返回该用户的所有的有效session
Map<String, ? extends Session> sessions = this.sessions.findByPrincipalName(userId + "");
for (Session session : sessions.values()) {
// 根据sessionId删除
this.sessions.deleteById(session.getId());
}
public interface FindByIndexNameSessionRepository<S extends Session> extends SessionRepository<S> {
String PRINCIPAL_NAME_INDEX_NAME =FindByIndexNameSessionRepository.class.getName()
.concat(".PRINCIPAL_NAME_INDEX_NAME");
Map<String, S> findByIndexNameAndIndexValue(String indexName, String indexValue);
default Map<String, S> findByPrincipalName(String principalName) {
return findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);
}
}
可以通过findByIndexNameAndIndexValue
方法自己建立key和session的索引信息。
session过期,销毁事件依赖于redis的key过期通知。事件对象通过spring的事件订阅发布机制来发布
SessionCreatedEvent 创建
SessionDestroyedEvent
|-SessionExpiredEvent 过期
|-SessionDeletedEvent 删除(用户主动 invalidate())
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.stereotype.Component;
@Component
public class SpringSessionListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringSessionListener.class);
@EventListener(SessionCreatedEvent.class)
@Async
public void sessionCreatedEvent(SessionCreatedEvent sessionCreatedEvent) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("session 创建:{}", sessionCreatedEvent.getSessionId());
}
}
@EventListener(SessionExpiredEvent.class)
public void sessionExpiredEvent(SessionExpiredEvent sessionCreatedEvent) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("session 到期:{}", sessionCreatedEvent.getSessionId());
}
}
@EventListener(SessionDeletedEvent.class)
public void sessionDeletedEvent(SessionDeletedEvent sessionCreatedEvent) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("session 删除:{}", sessionCreatedEvent.getSessionId());
}
}
}
比较麻烦,需要自己通过代码配置添加
需要添加SessionEventHttpSessionListenerAdapter到ioc, 通过这个bean的构造函数, 添加多个 HttpSessionListener 实现 SessionEventHttpSessionListenerAdapter
SessionEventHttpSessionListenerAdapter(List<HttpSessionListener> listeners)
但是这个Bean其实框架已经自动添加了, 再次添加会导致异常
曲线救国, 从IOC里面读取到这个bean, 通过反射, 对私有属性 listeners 添加监听器
@Configuration
public class SpringSessionConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringSessionConfiguration.class);
@Autowired SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter;
@PostConstruct
public void addHttpSessionListener() {
try {
Field field = SessionEventHttpSessionListenerAdapter.class.getDeclaredField("listeners");
field.setAccessible(Boolean.TRUE);
@SuppressWarnings("unchecked")
List<HttpSessionListener> listeners = (List<HttpSessionListener>) field.get(sessionEventHttpSessionListenerAdapter);
listeners.add(new SessionListener());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("添加SESSION监听器");
}
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
// @Bean //BeanDefinitionOverrideException
// public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
// SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter = new SessionEventHttpSessionListenerAdapter(Arrays.asList(new SessionListener()));
// return sessionEventHttpSessionListenerAdapter;
// }
}
默认客户端使用Cookie来存储session id。但是对于一些客户端来说,cookie不一定方便,可以通过 http header来携带cookie的id。
实现类 CookieHttpSessionIdResolver
使用Cookie(默认) HeaderHttpSessionIdResolver
使用Header
@Bean
public HttpSessionIdResolver httpSessionIdResolver() {
return HeaderHttpSessionIdResolver.xAuthToken(); // 使用 X-Auth-Token 解析Cookie
}
HeaderHttpSessionIdResolver 还支持自定义header的名称,代码及简单,可以自己阅读学习。
spring:session:expirations:1570672200000
spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:1
spring:session:sessions:d82bf2bb-deb3-474c-89bb-96c2bfa646c4
spring:session:sessions:expires:94b2ce1f-053e-4c20-a8b7-f4d69102a114
上述就是小编为大家分享的springboot中如何整合spring-session了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/KevinBlandy/blog/3118896