1. 程式人生 > 其它 >tomcat叢集中的session模擬

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隨便用