项目添加redis的session依赖
将分布式session存储在redis内:
打开项目的pom.xml,导入springboot对reids的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
添加如下依赖之后,可以增加一个配置类配置session存活时间:
@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)//session存活一小时
public class RedisConfig {
}
数据库服务器安装redis4.0.1
我这里数据库服务器只有一台,而应用程序服务器是多台的,所以会出现分布式的session获取不到的问题,这里将redis安装到唯一的数据库服务器中。
官网下载redis-4.0.1.tar.gz,放到服务器:
执行:
chmod - R 777 redis-4.0.1.tar.gz
tar -xvzf redis-4.0.1.tar.gz
cd redis-4.0.1
make
make install
cd src
./redis-server &
./redis-cli
安装并启动redis
本地部署测试
修改application.properties:
增加redis配置
#配置springboot对redis的依赖
spring.redis.host=127.0.0.1 # 这里之后还要修改
spring.redis.port=6379
spring.redis.database=10
#spring.redis.password=
#设置jedis连接池
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.min-idle=20
将用户登录的session模型类实现Serializable使redis可以序列化
之后测试登录:
登录成功
踩坑:
但是…坑爹的来了,测试获取验证码失败。
一步一步debug:发现redis内有数据,key也是正确的,但是获取却一直为null。
因为我的验证码的获取和用户输入是两个不同的页面,所以为了安全起见,session默认会被获取为null。
查阅资料得出,如果满足以下几点,session可能获取为null。
- 使用了springboot2.x,并且前后端资源是跨域的。
- 获取session的请求接口是post方式发送的
- 存储session和取得session是不同的两个页面。
满足以上三点,session的获取就会为null;
解决方案,编写配置类
@Configuration
public class SpringSessionConfig {
public SpringSessionConfig() {
}
@Bean
public CookieSerializer httpSessionIdResolver() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
// 取消仅限同一站点设置
cookieSerializer.setSameSite(null);
return cookieSerializer;
}
}
成功完成session跨页获取
至此为止,分布式session的问题被解决。不会出现用户登录失效的问题。
token方式替代基于cookie传递sessionId
项目代码中修改,将传统session方式替代。这样的好处是可以使用多个平台的兼容性。
//生成登录凭证token,每个用户不重复的uuid
String uuidToken = UUID.randomUUID().toString();
uuidToken = uuidToken.replace("-", "");
//建立token和登陆之后用户模型之间的联系
redisTemplate.opsForValue().set(uuidToken, userBO);
//设置超时时间一个小时
redisTemplate.expire(uuidToken,1, TimeUnit.HOURS);
// //将登录凭证加入到用户登陆成功的session
// this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true);
// this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userBO);
//下发token
return CommonReturnType.create(uuidToken);
前端判断登录成功之后存储token的代码:
var token = data.data;
//html5新出的token方式localStorage,比cookie session安全且没有容量限制
window.localStorage["token"]=token;
需要验证用户登录信息时,验证token是否存在:
var token = window.localStorage["token"];
if(token == null){
alert("没有登录,不能下单!");
window.location.href="login.html";
return false;
}
后端负责验证token信息是否正确,并从token获取用户模型:
String token = httpServletRequest.getParameterMap().get("token")[0];
if(StringUtils.isEmpty(token)){
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登录,不能下单");
}
//获取用户登录信息
UserBO userBO = (UserBO) redisTemplate.opsForValue().get(token);
if(userBO == null){
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登录,不能下单");
}
最后别忘了将token放到ajax请求的url中(参数拼上?token="+token):
//登录成功之后,将用户模型存入前台的localStorage
var token = data.data;
//html5新出的token方式localStorage,比cookie session安全且没有容量限制
window.localStorage["token"]=token;
//....................................................................
var token = window.localStorage["token"];//从前台localStorage获取token
$.ajax({
type: "POST",
url: "http://"+g_host+"/order/createorder?token="+token,
contentType: "application/x-www-form-urlencoded",
本地调试成功之后,部署到云端的数据库服务器中,因为我这里暂时还没有主从服务器,只有一台数据库服务器,所以把redis安装在了mysql数据库服务器中,两者共用一台服务器。
将两台应用程序服务器的application.properties都修改,增加如下的配置:
spring.redis.host=数据库服务器的私网ip。
之后重新启动两个应用程序jar包,调试,成功。
总结:
这篇文章主要解决的问题是:
用户登录信息的session经过分布式扩展之后可能获取不到的问题,从基于cookie传递sessionID的方式转化为基于token传输类似于sessionID的形式,并且使session迁移至公共的redis数据库中。
解决方式:
通过redis集中式缓存sessionID。并通过redisTemplate.opsForValue().set(uuidToken, userBO);实现session的存取。
UserBO userBO = (UserBO) redisTemplate.opsForValue().get(token);实现session的获取。