SpringSession header/cookie/attribute存放 SessionID(死磕)
疯狂创客圈 Java 高并发【 亿级流量聊天室实战】实战系列 【博客园总入口 】
架构师成长+面试必备之 高并发基础书籍 【Netty Zookeeper Redis 高并发实战 】
前言
Crazy-SpringCloud 微服务脚手架 &视频介绍:
Crazy-SpringCloud 微服务脚手架,是为 Java 微服务开发 入门者 准备的 学习和开发脚手架。并配有一系列的使用教程和视频,大致如下:
高并发 环境搭建 图文教程和演示视频,陆续上线:
中间件 | 链接地址 |
---|---|
Linux Redis 安装(带视频) | Linux Redis 安装(带视频) |
Linux Zookeeper 安装(带视频) | Linux Zookeeper 安装, 带视频 |
Windows Redis 安装(带视频) | Windows Redis 安装(带视频) |
RabbitMQ 离线安装(带视频) | RabbitMQ 离线安装(带视频) |
ElasticSearch 安装, 带视频 | ElasticSearch 安装, 带视频 |
Nacos 安装(带视频) | Nacos 安装(带视频) |
Crazy-SpringCloud 微服务脚手架 图文教程和演示视频,陆续上线:
组件 | 链接地址 |
---|---|
Eureka | Eureka 入门,带视频 |
SpringCloud Config | springcloud Config 入门,带视频 |
spring security | spring security 原理+实战 |
Spring Session | SpringSession 独立使用 |
分布式 session 基础 | RedisSession (自定义) |
重点: springcloud 开发脚手架 | springcloud 开发脚手架 |
SpingSecurity + SpringSession 死磕 (写作中) | SpingSecurity + SpringSession 死磕 |
小视频以及所需工具的百度网盘链接,请参见 疯狂创客圈 高并发社群 博客
场景和问题
由于 SpingSecurity + SpringSession 整合场景,涉及到SpringSession SessionID 存取的问题。
具体问题:
由 SpringSecurity 将sessionID放在了 request 的 attribute中, SpringSession 需要从 request 的 attribute中取得。
SpringSession 自带的 sessionId 存取器:
SpringSession中对于sessionId的存取相关的策略,是通过HttpSessionIdResolver这个接口来体现的。HttpSessionIdResolver有两个实现类:
1: SpringSession header 存取 SessionID
HeaderHttpSessionIdResolver,通过从请求头header中解析出sessionId。
具体地说,这个实现将允许使用HeaderHttpSessionIdResolver(String)来指定头名称。还可以使用便利的工厂方法来创建使用公共头名称(例如“X-Auth-Token”和“authenticing-info”)的实例。创建会话时,HTTP响应将具有指定名称和sessionId值的响应头。
如果要使用HeaderHttpSessionIdResolver ,方法为:
增加Spring Bean,类型为 HeaderHttpSessionIdResolver
import org.springframework.context.annotation.Bean; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import org.springframework.session.web.http.HeaderHttpSessionIdResolver; //设置session失效时间为30分钟 @EnableRedisHttpSession(maxInactiveIntervalInSeconds= 1800) public class HttpSessionConfig { @Bean public HeaderHttpSessionIdResolver headerHttpSessionIdResolver() { return new HeaderHttpSessionIdResolver("x-auth-token"); } }
sessionID放到header中可以实现共享了
更加完整的内容,参见 博文
2: SpringSession cookie 存取 SessionID
这种策略对应的实现类是CookieHttpSessionIdResolver,通过从Cookie中获取session。
下面为 CookieHttpSessionIdResolver 源码, 仅供参考。如果不做定制, SpringSession 默认的 IdResolver 就是这种了。
/* * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.session.web.http; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.session.web.http.CookieSerializer.CookieValue; public final class CookieHttpSessionIdResolver implements HttpSessionIdResolver { private static final String WRITTEN_SESSION_ID_ATTR = CookieHttpSessionIdResolver.class .getName().concat(".WRITTEN_SESSION_ID_ATTR"); private CookieSerializer cookieSerializer = new DefaultCookieSerializer(); //重点:从cookie 取得sessionid @Override public List<String> resolveSessionIds(HttpServletRequest request) { return this.cookieSerializer.readCookieValues(request); } //重点:设置 sessionid 到 cookie @Override public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) { if (sessionId.equals(request.getAttribute(WRITTEN_SESSION_ID_ATTR))) { return; } request.setAttribute(WRITTEN_SESSION_ID_ATTR, sessionId); this.cookieSerializer .writeCookieValue(new CookieValue(request, response, sessionId)); } @Override public void expireSession(HttpServletRequest request, HttpServletResponse response) { this.cookieSerializer.writeCookieValue(new CookieValue(request, response, "")); } /** * Sets the {@link CookieSerializer} to be used. * * @param cookieSerializer the cookieSerializer to set. Cannot be null. */ public void setCookieSerializer(CookieSerializer cookieSerializer) { if (cookieSerializer == null) { throw new IllegalArgumentException("cookieSerializer cannot be null"); } this.cookieSerializer = cookieSerializer; } }
3 SpringSession Attribute 存取 SessionID
如果要从 Attribute 存取 SessionID ,则必须实现一个定制的 HttpSessionIdResolver,代码如下:
package com.crazymaker.springcloud.standard.config; import com.crazymaker.springcloud.common.constants.SessionConstants; import lombok.Data; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.session.web.http.HeaderHttpSessionIdResolver; import org.springframework.session.web.http.HttpSessionIdResolver; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.List; @Data public class CustomedSessionIdResolver implements HttpSessionIdResolver { private RedisTemplate<Object, Object> redisTemplet = null; private static final String HEADER_AUTHENTICATION_INFO = "Authentication-Info"; private final String headerName; /** * Convenience factory to create {@link HeaderHttpSessionIdResolver} that uses * "X-Auth-Token" header. * * @return the instance configured to use "X-Auth-Token" header */ public static HeaderHttpSessionIdResolver xAuthToken() { return new HeaderHttpSessionIdResolver(SessionConstants.SESSION_SEED); } /** * Convenience factory to create {@link HeaderHttpSessionIdResolver} that uses * "Authentication-Info" header. * * @return the instance configured to use "Authentication-Info" header */ public static HeaderHttpSessionIdResolver authenticationInfo() { return new HeaderHttpSessionIdResolver(HEADER_AUTHENTICATION_INFO); } /** * The name of the header to obtain the session id from. * * @param headerName the name of the header to obtain the session id from. */ public CustomedSessionIdResolver(String headerName) { if (headerName == null) { throw new IllegalArgumentException("headerName cannot be null"); } this.headerName = headerName; } //重点,springsession 用来获得sessionID @Override public List<String> resolveSessionIds(HttpServletRequest request) { String headerValue = request.getHeader(this.headerName); if (StringUtils.isEmpty(headerValue)) { headerValue = (String) request.getAttribute(SessionConstants.SESSION_SEED); if (!StringUtils.isEmpty(headerValue)) { headerValue = SessionConstants.getRedisSessionID(headerValue); } } if (StringUtils.isEmpty(headerValue)) { headerValue = (String) request.getAttribute(SessionConstants.SESSION_ID); } return (headerValue != null) ? Collections.singletonList(headerValue) : Collections.emptyList(); } //重点,springsession 用来存放sessionid @Override public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) { response.setHeader(this.headerName, sessionId); } @Override public void expireSession(HttpServletRequest request, HttpServletResponse response) { response.setHeader(this.headerName, ""); } /** * hash的赋值去设置 * * @param key key * @param hkey hkey * @param value value */ public void hset(String key, String hkey, String value) { redisTemplet.opsForHash().put(key, hkey, value); } /** * hash的赋值去取值 * * @param key key * @param hkey hkey */ public String hget(String key, String hkey) { return (String) redisTemplet.opsForHash().get(key, hkey); } public Object getSessionId(String loginName) { return hget(SessionConstants.SESSION_ID + ":KEYS", loginName); } public void setSessionId(String loginName, String sid) { hset(SessionConstants.SESSION_ID + ":KEYS", loginName, sid); } }
SpringSession SessionID 存取 原理
牛逼的 SessionRepositoryFilter 过滤器: 位于 spring-session-core 包中,间接涉及到 SpringSession 的ID的获取和保存。
SessionRepositoryFilter 作为 SpringSession 的重要的一环,涉及两个非常重要的工作:
(1)请求处理前,SessionRepositoryFilter 通过多层函数调用, 从 HttpSessionIdResolver 中取得 SessionID。
(2)请求 处理完成后,SessionRepositoryFilter 负责session的提交(如:保存到redis),并且通过 HttpSessionIdResolver 输出sessionID。
近景图
具体,请关注 Java 高并发研习社群 【博客园 总入口 】
最后,介绍一下疯狂创客圈:疯狂创客圈,一个Java 高并发研习社群 【博客园 总入口 】
疯狂创客圈,倾力推出:面试必备 + 面试必备 + 面试必备 的基础原理+实战 书籍 《Netty Zookeeper Redis 高并发实战》
疯狂创客圈 Java 死磕系列
Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战
- Netty 源码、原理、JAVA NIO 原理
- Java 面试题 一网打尽
疯狂创客圈 【 博客园 总入口 】
来源:https://www.cnblogs.com/crazymakercircle/p/12037747.html