zoukankan      html  css  js  c++  java
  • Spring Session产生的sessionid与cookies中的sessionid不一样的问题 && httpOnly 设置不起作用的问题??

    背景:

      Springboot 2.0 (spring-session-data-redis + spring-boot-starter-web)

    需求: 

      通过cookies中取到的 sessionid 获取到 session

    预期效果:

      @Autowired

      private SessionRepositry sessionRepositry;

      ...

      Session session = sessionRespositry.findById(sessionId);

    真实结果: 获取到的session是null, 然而通过 request.getSession(); 可以获取到session, 说明 session是存在的.

    问题追踪后发现问题:

      cookie中的sessionId 与 session.getId() 不一样!!!

    DEBUG:

      1. 先看一看SpringSession是如何从Cookie中获取sessionid的! (相关类: org.springframework.session.web.http.DefaultCookieSerializer)

       

      2. 再看一看 useBase64Encoding 的值是啥, 首先看默认值

       3. 看看这些配置是在哪里被(赋值)确认的, 一路追踪到 org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration 配置类中

     看看 createDefaultCookieSerializer() 是如何实现的

      4. 从上面可以得出结论, 我们无法 通过配置文件 中 server.servlet.session.** 来配置 useBase64Encoding. 使 cookie中的 sessionid 与 session.getId() 保持一致

      

      5. 期间发现的另一个问题: 虽然 sessionCookieConfig 有httpOnly相关配置, 但这里并未将配置设入 cookieSerializer 中, 导致配置文件中的 server.servlet.session.cookie.httpOnly = false 不起作用

      

    解决方案:

      第一种方案:  通过配置 自定义的 CookieSerializer 来指定配置信息(如果觉得麻烦请直接看第二种方案), 如下

      a) 首先因为 SessionCookieConfig 接口中并没有定义 isUseBase64Encoding() 等接口, 导致缺少了部分配置, 所以我 自定义了一个 MySessionCookieConfig 接口继承了 SessionCookieConfig, 并写了一个默认实现 MyDefaultSessionCookieConfig

    package com.cardgame.demo.center.config;
    
    import javax.servlet.SessionCookieConfig;
    
    /**
     *
     * 补充 SessionCookie 中未定义的配置项
     * @author yjy
     * 2018-06-15 13:30
     */
    public interface MySessionCookieConfig extends SessionCookieConfig {
    
        String getDomainPattern();
    
        void setDomainPattern(String domainPattern);
    
        String getJvmRoute();
    
        void setJvmRoute(String jvmRoute);
    
        boolean isUseBase64Encoding();
    
        void setUseBase64Encoding(boolean useBase64Encoding);
    
    }
    MySessionCookieConfig
    package com.cardgame.demo.center.config;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    /**
     *
     * 涵盖 CookieSerializer 所有配置项
     * @author yjy
     * 2018-06-15 13:31
     */
    @Component
    @ConfigurationProperties(prefix = "server.servlet.session.cookie")
    public class MyDefaultSessionCookieConfig implements MySessionCookieConfig {
    
        private String name = "SESSION";
        private String path;
        private String domain;
        private String comment;
        private int maxAge = -1;
        private String domainPattern;
        private String jvmRoute;
        private boolean httpOnly = true;
        private boolean secure = false;
        private boolean useBase64Encoding = false;
    
        @Override
        public String getDomainPattern() {
            return domainPattern;
        }
    
        @Override
        public void setDomainPattern(String domainPattern) {
            this.domainPattern = domainPattern;
        }
    
        @Override
        public String getJvmRoute() {
            return jvmRoute;
        }
    
        @Override
        public void setJvmRoute(String jvmRoute) {
            this.jvmRoute = jvmRoute;
        }
    
        @Override
        public boolean isUseBase64Encoding() {
            return useBase64Encoding;
        }
    
        @Override
        public void setUseBase64Encoding(boolean useBase64Encoding) {
            this.useBase64Encoding = useBase64Encoding;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String getPath() {
            return path;
        }
    
        @Override
        public void setPath(String path) {
            this.path = path;
        }
    
        @Override
        public String getDomain() {
            return domain;
        }
    
        @Override
        public void setDomain(String domain) {
            this.domain = domain;
        }
    
        @Override
        public String getComment() {
            return comment;
        }
    
        @Override
        public void setComment(String comment) {
            this.comment = comment;
        }
    
        @Override
        public boolean isHttpOnly() {
            return httpOnly;
        }
    
        @Override
        public void setHttpOnly(boolean httpOnly) {
            this.httpOnly = httpOnly;
        }
    
        @Override
        public boolean isSecure() {
            return secure;
        }
    
        @Override
        public void setSecure(boolean secure) {
            this.secure = secure;
        }
    
        @Override
        public int getMaxAge() {
            return maxAge;
        }
    
        @Override
        public void setMaxAge(int maxAge) {
            this.maxAge = maxAge;
        }
    }
    MyDefaultSessionCookieConfig

      b) 利用 MyDefaultSessionCookieConfig 携带的配置, 自定义 CookieSerializer Bean

    package com.cardgame.demo.center.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
    import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
    import org.springframework.session.web.http.CookieSerializer;
    import org.springframework.session.web.http.DefaultCookieSerializer;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.ObjectUtils;
    
    import javax.servlet.ServletContext;
    import javax.servlet.SessionCookieConfig;
    
    /**
     *
     * @author yjy
     * 2018-06-08 14:53
     */
    @Slf4j
    @Configuration
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600, redisNamespace = "center")
    public class RedisSessionConfig implements ApplicationContextAware {
    
        @Bean
        public CookieSerializer cookieSerializer(ServletContext servletContext, MySessionCookieConfig sessionCookieConfig) {
            DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
            if (servletContext != null) {
                if (sessionCookieConfig != null) {
                    if (sessionCookieConfig.getName() != null)
                        cookieSerializer.setCookieName(sessionCookieConfig.getName());
                    if (sessionCookieConfig.getDomain() != null)
                        cookieSerializer.setDomainName(sessionCookieConfig.getDomain());
                    if (sessionCookieConfig.getPath() != null)
                        cookieSerializer.setCookiePath(sessionCookieConfig.getPath());
                    if (sessionCookieConfig.getMaxAge() != -1)
                        cookieSerializer.setCookieMaxAge(sessionCookieConfig.getMaxAge());
                    if (sessionCookieConfig.getDomainPattern() != null)
                        cookieSerializer.setDomainNamePattern(sessionCookieConfig.getDomainPattern());
                    if (sessionCookieConfig.getJvmRoute() != null)
                        cookieSerializer.setJvmRoute(sessionCookieConfig.getJvmRoute());
                    cookieSerializer.setUseSecureCookie(sessionCookieConfig.isSecure());
                    cookieSerializer.setUseBase64Encoding(sessionCookieConfig.isUseBase64Encoding());
                    cookieSerializer.setUseHttpOnlyCookie(sessionCookieConfig.isHttpOnly());
                }
            }
            if (ClassUtils.isPresent(
                    "org.springframework.security.web.authentication.RememberMeServices",
                    null)) {
                boolean usesSpringSessionRememberMeServices = !ObjectUtils
                        .isEmpty(this.context
                                .getBeanNamesForType(SpringSessionRememberMeServices.class));
                if (usesSpringSessionRememberMeServices) {
                    cookieSerializer.setRememberMeRequestAttribute(
                            SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
                }
            }
            return cookieSerializer;
        }
    
        private ApplicationContext context;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.context = applicationContext;
        }
    }
    RedisSessionConfig

      c) 修改配置文件配置

      

      d) 配置完成后重新启动, 再看 SpringHttpSessionConfiguration 加载的时候, 拿到了我们自定义的 CookieSerializer, 我想要的配置都有了!! 打开浏览器测试通过!!

       第二种方案: 拿到 Cookie 中的 sessionId 后, 手动解码, 再 通过 sessionRespositry.findById(sessionId); 获取session

        a) 解码的方案 从 DefaultSerializer 类中 copy 一个 , 如下:

        /**
         * Decode the value using Base64.
         * @param base64Value the Base64 String to decode
         * @return the Base64 decoded value
         * @since 1.2.2
         */
        private String base64Decode(String base64Value) {
            try {
                byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value);
                return new String(decodedCookieBytes);
            }
            catch (Exception e) {
                return null;
            }
        }

        b) 获取步骤:

          String cookieSessionId = "XXX";

          String sessionId = base64Decode(cookieSessionId);

          Session session = sessionRespositry.findById(sessionId);

        c) 搞定! (此方案未解决 httpOnly 不起效的问题, 如果要解决 httpOnly = false , 请看方案一)

  • 相关阅读:
    Vim Reference
    Java 8 Consumer、Supplier、Predicate、Function
    Java 8 Stream 用法
    Java 基础 Builder模式
    Spring/Spring-Boot 学习 使用自定义的ArgumentResolver
    架构之分布式图片存储系统架构
    微服务和SOA服务
    Centos 上 Tengine安装
    .NET平台上插拔姿势的AOP
    P1424 刷题记录
  • 原文地址:https://www.cnblogs.com/imyjy/p/9187168.html
Copyright © 2011-2022 走看看