0
点赞
收藏
分享

微信扫一扫

【计算机访问控制二】分布式session共享(序列化问题)

静守幸福 2022-04-13 阅读 4
redissession
  1. 在首次获取session后,将sessionid作为键将对应session域信息序列化后保存到redis数据库内
  2. 需要获取session域信息时,拿着request携带的cookie中的sessionId值向redis数据库查找对应的数据,并反序列化为Session对象
    /**
     * 保存session
     *
     * @param req 请求信心
     * @return 结果
     */
    @GetMapping("/saveSession")
    public AjaxResult saveSession(HttpServletRequest req){
        System.out.println(new Date()+":::"+req.getSession().getId());
        HttpSession session = req.getSession();
        session.setAttribute("num",num++);
        String sessionString = JSON.toJSONString(session);
        redisFullUtil.set(session.getId(),sessionString);
        return AjaxResult.success(session.getId()+"::"+session.getAttribute("num"));
    }


    /**
     * 获取保存的session
     *
     * @param req 请求信息
     * @return 结果
     */
    @GetMapping("/getSession")
    public AjaxResult getSession(HttpServletRequest req){
        System.out.println(new Date()+":::"+req.getSession().getId());
        String sessionString = redisFullUtil.get(req.getSession().getId());
        HttpSession session = JSON.parseObject(sessionString, HttpSession.class);
        return AjaxResult.success(session.getAttribute("num"));
    }

程序运行后直接报错

在这里插入图片描述

 原因是redis序列化依赖于set和get方法,session中有些数据属性是没有的,

spring-session-data-redis

转变思路一番搜索,现有技术是如何实现session分离,发现spring-session-data-redis就是专门做这件事的,还帮我们完成了大部分的工作,只需要做一些简答的配置工作即可完成session的分离

1. pom引入依赖

<!-- spring boot 与 redis 应用的基本配置 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- spring session 与redis 应用环境基本配置, 需要开启redis后才可以使用, 不然启动会报错 -->
<dependency>
   <groupId>org.springframework.session</groupId>
   <artifactId>spring-session-data-redis</artifactId>
</dependency>

2. 配置: application.yml

spring:
  redis:
    database: 0
    host: 你的redisip
    port: 6379
    password: 你的redis密码
    pool:
      max-idle: 8
      min-idle: 0
      max-active: 8
      max-wait: 1
    timeout: 5000
  session:
    store-type: redis
    
    
redis:
   hostname: 你的redisip
   port: 6379
   pawssword: 你的redis密码

3. 增加配置类

SessionConfig.Class

package org.huber.sharesession.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.DefaultCookieSerializer;

import javax.validation.Valid;

/**
 * @Classname SessionConfig
 * @Description TODO
 * @Date 2020/1/17 12:43
 * @Author by Ren Jie
 */
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SessionConfig {

    @Value("{$redis.hostname}")
    private String hostName;

    @Value("${redis.port}")
    private int port;


    @Bean
    public RedisStandaloneConfiguration connectionFactory() {
        RedisStandaloneConfiguration jedisConnectionFactory = new RedisStandaloneConfiguration();
        jedisConnectionFactory.setHostName(hostName);
        jedisConnectionFactory.setPort(port);
        return jedisConnectionFactory;
    }

    @Bean
    public DefaultCookieSerializer defaultCookieSerializer(){
        DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
        defaultCookieSerializer.setCookiePath("/");
        return defaultCookieSerializer;
    }
}

核心就在这个反序列化上 

package org.huber.sharesession.config;

import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;

/**
 * @Classname SessionInitializer
 * @Description TODO
 * @Date 2020/1/17 12:47
 * @Author by Ren Jie
 */
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer {

    public SessionInitializer(){
        super(SessionConfig.class);
    }
}

结果测试

  1. 开启会话,获取sessionid
  2. 重启服务器
  3. 再次访问后台,获取sessionid
    如果是同一个sessionid,则说明session分离成功
package org.huber.sharesession.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
 * @Classname SessionController
 * @Description TODO
 * @Date 2020/1/17 12:49
 * @Author by Ren Jie
 */
@RestController
@RequestMapping("/shareSession/session")
public class SessionController {

    @GetMapping("/setSession")
    public String setSession(HttpServletRequest req){
        String sessionId = req.getSession().getId();
        System.out.println("sessionId = " + sessionId);
        return sessionId;
    }
}

演示如下:
 

完了还是可能有问题:

我们来看看redis的序列化:

对redis进行配置,实现发序列化,因为reids序列化已经实现了,这里就不用再对序列化进行研究了(redis序列化见文章:DefaultSerializer requires a Serializable payload but received an object of type [select.system.dto.User] - 叶语婷 - 博客园)。

这里对key和value分别进行反序列化,进而在redis中生成字符型的key和对象型value。

/**
 * @author yeyuting
 * @create 2021/2/19
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(new JdkSerializationRedisSerializer());
        //value hashmap序列化
        template.setHashValueSerializer(redisSerializer);
        //key haspmap序列化
        template.setHashKeySerializer(redisSerializer);

        return template;
        }
    }

执行后redis中是这样的

 可知,key反序列话成功了,但是value对象反序列化失败了,接着就将注意力转移到value对象的反序列化中来。

当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。 用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口。在存储内容时,除了属性的内容外还存了其它内容在里面,总长度长,且不容易阅读。我们要求是存储的数据可以方便查看,也方便反系列化,方便读取数据。JacksonJsonRedisSerializer和GenericJackson2JsonRedisSerializer,两者都能系列化成json,但是后者会在json中加入@class属性,类的全路径包名,方便反系列化。前者如果存放了List则在反系列化的时候如果没指定TypeReference则会报错java.util.LinkedHashMap cannot be cast to 。

因此保险起见,我们将JdkSerializationRedisSerializer换成GenericJackson2JsonRedisSerializer,修改后的代码如下:

/**
 * @author yeyuting
 * @create 2021/2/19
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        //value hashmap序列化
        template.setHashValueSerializer(redisSerializer);
        return template;
        }
    }

执行后redis缓存内容正常显示,如下:

原理就是将httpSession替换成自己实现的httpsession

参考:1、Redis应用:session分离 【session序列化失败 JSONException异常 spring-session-data-redis实现】_奋斗zhe的博客-CSDN博客

 2、Java简单实现session保存到redis的方法示例_李修睿的博客-CSDN博客_java session存入redis

3、 

举报

相关推荐

0 条评论