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 面試題 一網打盡
- 瘋狂創客圈 【 博客園 總入口 】