Spring-Session + Redis实现分布式Session管理

在传统的单tomcat容器中,用户访问服务器,tomcat会为该用户创建一个session,并将该session的sessionid通过cookie传送给用户,当用户再次访问时如果cookie中携带sessionid,则会到tomcat中寻找指定session。如下图所示:

但是当tomcat容器不止一台时,这种模式下就会出现问题。在分布式环境下,后台服务器往往有多台,用户在访问后台资源时往往会通过如nginx等应用服务器进行负载均衡。比如说你此次登录的时候被分配到tomcat A,那你的session将会存储到tomcat A,但是你接下来的某一次操作中需要在session中获取用户认证信息,而负责这个操作的是tomcat B,tomcat B根据你的sessionid在tomcat B中找不到你的session,这个时候你就无法完成这些需要身份认证才能执行的操作。即便你重新登录,但每次访问的tomcat都有可能不同,这有可能导致你永远执行不了某一些操作。如下图所示:

为了解决上面所说的问题,就需要统一对session进行管理,而基于redis本身的特性,使得redis在做分布式会话管理的时候有很大的优势,因为redis本身存储的数据就是key-value结构,同时redis对于数据的存储还可以设置过期时间。我们可以将session迁移到redis进行统一管理,只需要存入redis中的key(sessionid)返回给用户即可。而对于返回的形式,这里介绍两种:基于cookie传输sessionid基于token传输sessionid

以下操作基于SpringBoot2.0.5版本。

传统模式

登录操作:

/**
 * @author: Charviki
 * @create: 2019-09-20 9:40
 **/
@Controller
@RequestMapping("user")
public class UserController {

    @Autowired
    private HttpServletRequest request;

    @PostMapping
    public void login(@RequestParam("username")String username,
                      @RequestParam("password")String password){
        // 这里省略用户名密码校验过程

        // 模拟session存储用户校验信息
        request.getSession().setAttribute("user","用户已登录");
    }
}

比如在下单环境下,需要校验用户是否登录:

/**
 * @author: Charviki
 * @create: 2019-09-20 10:39
 **/
@Controller
@RequestMapping("order")
public class OrderController {

    @Autowired
    private HttpServletRequest request;

    @PostMapping
    public void createOrder(){
        // 获取session中的用户信息
        Object user = request.getSession().getAttribute("user");
        if (user == null){
            // 校验不通过
        }
        // 这里省略后端校验用户信息和下单过程
    }
}
分布式环境下

以下两种方式的实现都是基于传统模式的代码。

基于cookie传输sessionid

在springboot中引入依赖:

<!-- 这里使用的是SpringBoot2.0.5 -->
<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>
    <version>2.0.5.RELEASE</version>
</dependency>

在SpringBoot配置文件中指定redis地址(默认为localhost),这里使用的是application.yml:

spring:
  redis:
    host: 192.168.127.200

新增以下配置类:

@Component
// session过期时间
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
public class RedisConfig {
}

这是SpringBoot自身的一套实现,在不改变现有代码的情况下,通过标准的servlet api调用session,在底层就会通过Spring Session得到的,并且会存储到Redis或其他你所选择的数据源中进行集中管理。

基于token传输sessionid

这里先说明一个问题,基于token传输sessionid和基于cookie传输sessionid其实实现效果差不多,但为什么还要有基于token的实现呢?因为对于现在的企业级应用来说,可能有小程序、安卓、网页端等共用同一套服务器程序,其他设备可能不存在cookie域的说法,所有需要基于token传输sessionid的实现。

需要在SpringBoot配置文件中指定redis地址:

spring:
  redis:
    host: 192.168.127.200

修改登录代码:

@RestController
public class UserController {
    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("/user")
    public String login(@RequestParam("username")String username,
                      @RequestParam("password")String password){
        // 这里省略用户名密码校验过程

        // 生成token
        String uuidToken = UUID.randomUUID().toString().replace("-","");
        // 将用户信息存入redis
        redisTemplate.opsForValue().set("user","用户已登录");
        // 设置过期时间为一小时
        redisTemplate.expire(uuidToken,1, TimeUnit.HOURS);
        // 返回给前端token(sessionid)信息
        return uuidToken;
    }
}

在下单环境中,更改校验代码如下:

@Controller
@RequestMapping("order")
public class OrderController {

    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping
    public void createOrder(@RequestParam("token")String token){
        // 获取用户信息
        Object user = redisTemplate.opsForValue().get("token");
        if (user == null){
            // 校验不通过
        }
        // 这里省略后端校验用户信息和下单过程
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值