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