zoukankan      html  css  js  c++  java
  • 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) 聊天程序【 亿级流量】实战 开源项目实战
  • 相关阅读:
    随笔:判断一个范围内有多少质数,分别是多少
    随笔:判断一个整数是否是质数,如果不是质数,那么因数表达式是什么
    随笔:Python发送SMTP邮件方法封装
    Python基础学习:打印九九乘法表
    随笔:docker学习笔记(包括了基础学习和制作运行jar包的docker镜像,还有centos7防火墙这个坑)
    随笔:测试心得
    随笔:docker安装
    Python基础:Python连接MySQL数据库方法封装2
    随笔:Python打印临时日志、清空临时日志
    radio点击一下选中,再点击恢复未选状态
  • 原文地址:https://www.cnblogs.com/crazymakercircle/p/12037747.html
Copyright © 2011-2022 走看看