SpringSession header/cookie/attribute存放 session id


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中可以實現共享了

img

更加完整的內容,參見 博文

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 高並發實戰

img


瘋狂創客圈 Java 死磕系列

  • Java (Netty) 聊天程序【 億級流量】實戰 開源項目實戰


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM