【身份认证与控制二】分布式session共享(序列化问题)

  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"));}

程序运行后直接报错

com.alibaba.fastjson.JSONException: illegal setter

在这里插入图片描述

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

spring-session-data-redis

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

1. pom引入依赖


org.springframework.bootspring-boot-starter-data-redis

org.springframework.sessionspring-session-data-redis

2. 配置: application.yml

spring:redis:database: 0host: 你的redisipport: 6379password: 你的redis密码pool:max-idle: 8min-idle: 0max-active: 8max-wait: 1timeout: 5000session:store-type: redisredis:hostname: 你的redisipport: 6379pawssword: 你的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;@Beanpublic RedisStandaloneConfiguration connectionFactory() {RedisStandaloneConfiguration jedisConnectionFactory = new RedisStandaloneConfiguration();jedisConnectionFactory.setHostName(hostName);jedisConnectionFactory.setPort(port);return jedisConnectionFactory;}@Beanpublic 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 {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory factory) {RedisTemplate template = new RedisTemplate<>();RedisSerializer 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 {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory factory) {RedisTemplate template = new RedisTemplate<>();RedisSerializer 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、 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部