tomcat集群中的session模拟

Cookie和Session的区别(面试必备)_chen13333336677的博客-CSDN博客_cookie和session区别面试

1 什么是session

浏览器每次访问,服务器都会为每个用户创建一个独立的HttpSession对象,第一次访问服务器时,请求中没有携带任何标识,所以服务器会创建一个新的session对象,并且生成一个SessionID;这个SessionID在响应浏览器的时候会被装进cookie中,从而被保存到浏览器中;当用户再一次访问服务器时,请求中会携带着cookie中的SessionID去访问;服务器会根据这个SessionID去查看是否有对应的Session对象;有就拿出来使用,没有就创建一个Session。

通常在Session中存储用户或菜单等信息

2 session的获取方式

2.1 从HttpServletRequest直接获取

2.2 @SessionAttribute注解获取

Spring MVC @SessionAttributes注解 - Just_Do - 博客园 (cnblogs.com)

3 Session返回流程

3.1 后端返回代码

后端使用map保存session并返回给前端

  • 拓展:map在JDK1.8下的新写法

3.2 Session返回图解

用户首次访问服务器,服务器生成session,然后生成cookie保存在用户的浏览器上,直到设定的过期时间达到或者用户主动清除cookie

tomcat服务器上储存session的容器如图

在存活期内,cookie会跟随每一次request

  • 拓展:请求头中的User-Agent给出了前端访问类型,如果需要电脑模拟手机访问服务器,通常就是修改这个属性值

3.3 存在的问题

这一整套流程中,session被保存在tomcat服务器上,因此,每当tomcat服务器重启时,session就会被清空,这就会造成用户需要重新输入登录密码等问题

除此之外,如果Nginx使用了ip_hash策略,并且后续tomcat服务器集群数量发生了变化,那么就会导致session与用户的cookie

这一切的源头都是因为session保存在了服务器上,我们必须想办法把session保存在一个持久化容器上,比如mysql,比如redis的rdb

4 手动创建session 解决session随tomcat服务器被清空的问题

假设要实现用户登录模块,此时可以利用hutool工具类生成一个MD5随机码充当sessionID,然后手动创建一个cookie返回给前端

代码如下

生成的MD5随机码如图

前端响应头如图

这个MD5码的目的就是做一个Jsession的效果,下次再访问时,后端能根据request携带的MD5码给出数据

由于这个MD5码是手动创建的,所以我们可以将其存到各种持久化容器中,这就解决了问题

5 储存自定义session到redis中

首先设置redis.windows.conf,把bind绑定的ip注释掉,不需要绑定本机

然后使用一个客户端工具来控制redis——another-redis-desktop manager

这个软件的GUI做的好看一点,也方便操作redis,不过更建议使用命令行,毕竟多敲才能熟练

在controller声明stringRedisTemplate,这个对象能提供操作redis的方法,利用这个对象的方法,将手动模拟的session存入redis

6 springboot项目下 配置web拦截器 实现对前端cookie的验证

以用户登录模块为例子,项目为springboot项目

当前端发送访问请求时,拦截下来,然后从请求头中取出cookie,拿到key为“sid”的cookie的value,再拿到redis去判断是否存在对应的value

存在则让用户直接登录成功,否则让用户重新输入账号密码

编写配置类

编写拦截器

@Component
public class SessionInterceptor implements HandlerInterceptor {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean isLogin = false;
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for (Cookie cookie : cookies) {
                if("sid".equals(cookie.getName())){
                    String value = cookie.getValue();
                    String sessionKey = RedisConstants.USER_SESSION_KEY + value;
                    String UserInfoStr = stringRedisTemplate.opsForValue().get(sessionKey);
                    if(StringUtils.hasLength(UserInfoStr)){
                        isLogin = true;
                    }
                }
            }
        }

        if(!isLogin){
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json");
            PrintWriter writer = response.getWriter();
            HashMap<String, Object> map = new HashMap<>();
            map.put("status",10003);
            map.put("msg","请先登录!");
            String string = JSON.toJSONString(map);
            writer.write(string);
            writer.flush();
        }
        return isLogin;
    }
}

7 手动模拟的意义

上面手动模拟了session,再使用拦截器,就摆脱了tomcat的session,不再依赖tomcat服务器的状态,因此session不在担心tomcat服务器重启而被清空,同时session保存在redis中,就实现了tomcat服务器集群之间的共享,也不再担心Nginx使用ip_hash策略时,session与用户cookie对不上的问题

分布式锁的创建思维也是这样,自定义锁,只要解决锁的原子性问题,就能使用

8 spring提供的SpringSession干掉手动模拟session

spring已经把这个知识点整合了,给了一个springsession,我们不再需要手动封装这些东西

Spring Session 是Spring家族中的一个子项目,Spring Session提供了用于管理用户会话信息的API和实现。它把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题,Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中

8.1 载入maven依赖

这个封装好的东西原理是利用SessionAttributeListener来监听session对象的操作,当Session生成时,将会自动将Session的JsessionID和保存到session中的属性保存至redis中

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 使用SessionAttributeListener 来监听Session对象的操作,当Session生成时,将会自动将session的JsessionID和保存到session中的属性保存至redis中 -->
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
</dependency>

8.2 springboot项目的yml的配置

spring:
  redis:
    host: 127.0.0.1
    password: javasm
    port: 6379
    timeout: 3000
  session:
store-type: redis

8.3 使用springsession

springBoot session使用_灰猫-CSDN博客_springboot使用session

SpringSession使用_LililililililMeng的博客-CSDN博客_springsession使用

spring-session之一:简介、使用及实现原理 - duanxz - 博客园 (cnblogs.com)

直接在controller的方法中使用HttpSession

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

import javax.servlet.http.HttpSession;

@RestController
public class TestController {

   @GetMapping("/set")
   public String setSession(HttpSession session){
       session.setAttribute("msg","Hello");
       return "OK";
   }

   @GetMapping("/get")
   public String getSession(HttpSession session){
       return (String)session.getAttribute("msg");
   }
}

8.4 springsession保存到redis的原理

redis是一个大map,结构如图,而springsession有点取巧,每一个session都设为一个key,然后设置过期时间,这样就能很方便的进行管理

虽然这会导致产生大量的key,但是对于redis来说,可以储存几十亿的key,所以spring随便用

posted @ 2021-10-10 21:26  夏·舍  阅读(79)  评论(0编辑  收藏  举报