千家信息网

springboot中如何整合spring-session

发表于:2025-02-08 作者:千家信息网编辑
千家信息网最后更新 2025年02月08日,这期内容当中小编将会给大家带来有关springboot中如何整合spring-session,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。springboot中整合
千家信息网最后更新 2025年02月08日springboot中如何整合spring-session

这期内容当中小编将会给大家带来有关springboot中如何整合spring-session,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。


springboot中整合使用spring-session

spring-session

它可以替代 HttpSesession。而且改动极小,对应用透明。底层可以使用内存,Redis等存储Session信息。通过Redis这种方式可以做到Session共享,在集群环境中所有节点共享Session。

文档 https://docs.spring.io/spring-session/docs/current/reference/html5/

SpringBoot整合

使用 spring-session-data-redis,一定要先整合redis到项目。

依赖
        org.springframework.session        spring-session-data-redis
配置

配置类: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就是了。

controller
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")@Controllerpublic 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");        }}
View
                                        Test                                Hello ${name}              

Cookie属性的修改

默认情况下,客户端使用Cookie来存储会话id

查看响应信息,可以看到设置了session的cookie
Connection: keep-aliveContent-Encoding: gzipContent-Language: zh-CNContent-Type: text/html;charset=UTF-8Date: Thu, 17 Oct 2019 08:57:07 GMTServer: nginxSet-Cookie: PHPSESSIONID=Y2YwMDM1YjctMjBiYy00OWRiLWI5NGItZjFmNDU4ZDcxNThm; Max-Age=36000; Expires=Thu, 17 Oct 2019 18:57:07 GMT; Path=/; HttpOnly; SameSite=LaxTransfer-Encoding: chunked
可以通过配置修改cookie的信息。
server:  servlet:        session:          cookie:            name: PHPSESSIONID #cookie名称            domain:  # 域            path:  # 路径            comment: # 备注            httpOnly: # 是否仅用于http传输            secure: # 是否仅在SSL的情况下传输            maxAge: # 生命周期
也可以通过配置修改cookie信息

自定义 CookieSerializer 到IOC。

@Beanpublic CookieSerializer cookieSerializer() {        DefaultCookieSerializer serializer = new DefaultCookieSerializer();        serializer.setCookieName("JSESSIONID");         serializer.setCookiePath("/");         serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");         return serializer;}

Session的索引

通俗的理解就是,可以建立key和session的索引,根据某些key获取到session。

FindByIndexNameSessionRepository

需求:根据用户id,获取到它的会话

用户登录成功后,把它的id以固定的key存入到Session

value仅仅接受字符串

@AutowiredFindByIndexNameSessionRepository sessions;.... 代码省略Integer userId = user.getId();// 往session中存入用户的id信息request.getSession().setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, userId + "");
根据用户id检索它的session信息
@AutowiredFindByIndexNameSessionRepository sessions;.... 代码省略Integer userId = user.getId();// 返回该用户的所有的有效sessionMap sessions = this.sessions.findByPrincipalName(userId + "");for (Session session : sessions.values()) {        // 根据sessionId删除        this.sessions.deleteById(session.getId());}
FindByIndexNameSessionRepository 接口源码
public interface FindByIndexNameSessionRepository extends SessionRepository {        String PRINCIPAL_NAME_INDEX_NAME =FindByIndexNameSessionRepository.class.getName()                        .concat(".PRINCIPAL_NAME_INDEX_NAME");        Map findByIndexNameAndIndexValue(String indexName, String indexValue);                default Map 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;@Componentpublic 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());                }        }}
使用Servlet的监听器

比较麻烦,需要自己通过代码配置添加

  • 需要添加SessionEventHttpSessionListenerAdapter到ioc, 通过这个bean的构造函数, 添加多个 HttpSessionListener 实现 SessionEventHttpSessionListenerAdapter SessionEventHttpSessionListenerAdapter(List listeners)

  • 但是这个Bean其实框架已经自动添加了, 再次添加会导致异常

  • 曲线救国, 从IOC里面读取到这个bean, 通过反射, 对私有属性 listeners 添加监听器

@Configurationpublic 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 listeners = (List) 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;//      }}

使用Http Header来解析session id

默认客户端使用Cookie来存储session id。但是对于一些客户端来说,cookie不一定方便,可以通过 http header来携带cookie的id。

Session的解析依赖于一个接口: HttpSessionIdResolver

实现类 CookieHttpSessionIdResolver 使用Cookie(默认) HeaderHttpSessionIdResolver 使用Header

配置通过 HttpHeader 来解析session id
@Beanpublic HttpSessionIdResolver httpSessionIdResolver() {        return HeaderHttpSessionIdResolver.xAuthToken();  // 使用 X-Auth-Token 解析Cookie}

HeaderHttpSessionIdResolver 还支持自定义header的名称,代码及简单,可以自己阅读学习。

spring-session在redis中数据的存储结构

spring:session:expirations:1570672200000spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:1spring:session:sessions:d82bf2bb-deb3-474c-89bb-96c2bfa646c4spring:session:sessions:expires:94b2ce1f-053e-4c20-a8b7-f4d69102a114

上述就是小编为大家分享的springboot中如何整合spring-session了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

0