会话(session)管理是Java Web应用不可或缺的功能,使用云数据库Redis版和Spring Session可以便捷地实现会话管理。

前提条件

  • 创建用于保存会话的云数据库Redis实例。
  • 如果通过内网连接Redis实例,Redis实例与部署应用的ECS实例需在同一VPC中,或者同属经典网络且在同一地域。
  • 已将ECS实例的内网IP地址设置到Redis实例的白名单中

背景信息

浏览网页通常依赖于HTTP协议,但HTTP的特性之一是无状态,即不会保存事务处理进度,在交互场景中没有记忆能力。如果仅有HTTP协议,用户在同一网站浏览两个不同页面时进行的某些交互性操作将毫无意义。例如,访问淘宝时,如果用户同时打开购物车页和商品的详情页,在详情页将若干心仪的商品加入购物车,然后再刷新购物车页,会发现之前加入购物车的商品都没有保存成功。因此,Web应用需要使用会话管理技术,例如Cookie和Session,将用户会话完整保存下来,甚至在分布式服务中共享。

Cookie是客户端技术,将用户信息保存在本地,信息容量受限于用户使用的浏览器,且有一定的安全风险。Session是服务端技术,将用户信息保存在服务端,信息容量可扩展,同时在一定程度上降低了安全风险,弥补了Cookie技术的不足。使用Sping Session与云数据库Redis版可以轻松实现Web应用中的会话管理。

图 1. Web应用中的会话管理
Web应用中的Session管理

Spring Session是近年来较为流行的轻量级框架Spring Boot下的一个项目,提供用户会话信息管理服务和相关API。Spring Session可以用透明的方式集成HttpSession,从而实现Web应用中的会话管理,该方案有以下优势:

  • 支持集群会话(Clustered Sessions)

    Spring Session使支持集群会话变得很简单,不必依赖于应用程序容器(例如Tomcat等)提供的特定解决方案。

  • 支持多用户会话

    Spring Session支持在单个浏览器实例中管理多个用户的会话,例如谷歌浏览器中多用户同时登录的情况。

  • 支持RESTful API

    Spring Session允许在标头(header)中提供会话ID以支持RESTful API。

云数据库Redis版在该方案被用于储存用户会话信息,提供高性能的会话信息存取服务。

操作步骤

  1. 使用Spring Initializr创建Spring Boot Web项目。
    默认生成的项目目录如下。Spring Boot Web项目目录
  2. pom.xml中配置Maven依赖。
    <dependencies>
      <!-- Spring boot -->  <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
      </dependency>
      <!-- Spring Web -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <!-- redis -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-redis</artifactId>
      </dependency>
      <!-- Spring session -->
      <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
      </dependency>
    </dependencies>
  3. src/main/resources/application.properties中配置Redis信息。
    说明 此处需要配置云数据库Redis实例的连接地址,您可以根据需要选择内网连接地址或外网连接地址。
    spring.session.store-type = REDIS
    spring.session.redis.flush-mode = on-save
    spring.session.redis.namespace = spring:session
    spring.redis.host = r-bp1xxxxxxxxxxxxxxxx.redis.rds.aliyuncs.com
    spring.redis.password = myPassword
    spring.redis.port = 6379
  4. 配置src/main/java/com.example.demo/DemoApplication.java,开启Spring Session。
    package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
    
    @SpringBootApplication
    @EnableRedisHttpSession    
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }
  5. 编写业务逻辑代码。
    下方是使用Controller来处理HTTP请求的示例代码。
    package com.example.demo.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import java.util.UUID;
    
    @Controller
    @RequestMapping("/")
    public class SessionController{
        @RequestMapping(value="/getSessionId")
        @ResponseBody
        public String getSessionId(HttpServletRequest request){
            int port = request.getLocalPort();
            String sessionId = request.getSession().getId();
            String userId = request.getSession().getAttribute("userId").toString();
    
            return "端口:" + port
                    +  "<br/>sessionId:" + sessionId
                    +"<br/>属性userId:"+ userId;
        }
        @RequestMapping(value="/setSessionId")
        @ResponseBody
        public String setSessionId(HttpServletRequest request){
            String userId = UUID.randomUUID().toString().replaceAll("-", "");
            request.getSession().setAttribute("userId", userId);
            return "Session 设置成功.<br />userId:" + userId;
        }
    }
  6. 启动Spring Boot服务。
  7. 测试会话设置和读取。
    1. 在浏览器中访问localhost:8080/setSessionId
      测试会话设置
    2. 在浏览器中访问localhost:8080/getSessionId
      测试会话读取

案例总结

只要业务中有会话管理需求,都可以使用云数据库Redis版存储会话信息。Spring Boot等一些当前流行的框架可以帮助您更方便、快捷地集成云Redis,轻松实现分布式会话管理。