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) 聊天程序【 亿级流量】实战 开源项目实战
原文地址:https://www.cnblogs.com/crazymakercircle/p/12037747.html