最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring-redis-session 自定義 key 和過期時間

 更新時間:2019年12月30日 08:32:47   作者:sanri1993  
這篇文章主要介紹了spring-redis-session 自定義 key 和過期時間,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

對于分布式應用來說,最開始遇到的問題就是 session 的存儲了,解決方案大致有如下幾種

  • 使用 spring-session 它可以把 session 存儲到你想存儲的位置,如 redis,mysql 等
  • 使用 JWTs ,它使用算法來驗證 token 的合法性,是否過期,并且 token 無法被偽造,信息也是無法被篡改的

本文內(nèi)容主要說 spring-session 使用 redis 來存儲 session ,實現(xiàn)原理,修改過期時間,自定義 key 等

spring-session 對于內(nèi)部系統(tǒng)來說還是可以的,使用方便,但如果用戶量上來了的話,會使 redis 有很大的 session 存儲開銷,不太劃算。

使用

使用起來比較簡單,簡單說一下,引包,配置,加注解 。如下面三步,就配置好了使用 redis-session

<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>
</dependency>

spring.redis.host=localhost 
# 其它 超時,端口,庫,連接池,集群,就自己去找了
@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 1800)

測試:因為是在 getSession 的時候才會創(chuàng)建 Session ,所以我們必須在接口中調(diào)用一次才能看到效果

@GetMapping("/sessionId")
public String sessionId(){
  HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  HttpSession session = request.getSession();
  session.setAttribute("user","sanri");
  return session.getId();
}

它的存儲結果如下

hash spring:session:sessions:e3d4d84f-cc9f-44d5-9199-463cd9de8272
string spring:session:sessions:expires:e3d4d84f-cc9f-44d5-9199-463cd9de8272
set spring:session:expirations:1577615340000
第一個 hash 結構存儲了 session 的一些基本信息和用戶設置的一些屬性信息

creationTime 創(chuàng)建時間

lastAccessedTime 最后訪問時間

maxInactiveInterval 過期時長,默認是 30 分鐘,這里保存的秒值

sessionAttr:user 這是我通過 session.setAttribute 設置進去的屬性

第二個 string 結構,它沒有值,只有一個 ttl 信息,標識這組 key 還能活多久,可以用 ttl 查看

第三個 set 結構,保存了所以需要過期的 key

實現(xiàn)原理

說明:這個實現(xiàn)沒多少難度,我就照著源碼念一遍了,就是一個過濾器的應用而已。

首先從網(wǎng)上了解到,它是使用過濾器來實現(xiàn)把 session 存儲到 redis 的,然后每次請求都是從 redis 拿到 session 的,所以目標就是看它的過濾器是哪個,是怎么存儲的,又是怎么獲取的。

我們可以從它唯一的入口 @EnableRedisHttpSession 進入查看,它引入了一個 RedisHttpSessionConfiguration 開啟了一個定時器,繼承自 SpringHttpSessionConfiguration ,可以留意到 RedisHttpSessionConfiguration 創(chuàng)建一個 Bean RedisOperationsSessionRepository repository 是倉庫的意思,所以它就是核心類了,用于存儲 session ;那過濾器在哪呢,查看SpringHttpSessionConfiguration 它屬于 spring-session-core 包,這是一個 spring 用來管理 session 的包,是一個抽象的概念,具體的實現(xiàn)由 spring-session-data-redis 來完成 ,那過濾器肯定在這里創(chuàng)建的,果然可以看到它創(chuàng)建一個 SessionRepositoryFilter 的過濾器,下面分別看過濾器和存儲。

SessionRepositoryFilter

過濾器一定是有 doFilter 方法,查看 doFilter 方法,spring 使用 OncePerRequestFilter 把 doFilter 包裝了一層,最終是調(diào)用 doFilterInternal 來實現(xiàn)的,查看 doFilterInternal 方法

實現(xiàn)方式為使用了包裝者設計把 request 和 response 響應進行了包裝,我們一般拿 session 一般是從 request.getSession() ,所以包裝的 request 肯定要重寫 getSession ,所以可以看 getSession 方法來看是如何從 redis 獲取 session ;

前面都是已經(jīng)存在 session 的判斷相關,關鍵信息在這里

S session = SessionRepositoryFilter.this.sessionRepository.createSession();

這里的 sessionRepository 就是我們用來存取 session 的 RedisOperationsSessionRepository 查看 createSession 方法

RedisOperationsSessionRepository

// 這里保存了在 redis 中 hash 結構能看到的數(shù)據(jù)
RedisSession redisSession = new RedisSession();

this(new MapSession());
this.delta.put(CREATION_TIME_ATTR, getCreationTime().toEpochMilli());
this.delta.put(MAX_INACTIVE_ATTR, (int) getMaxInactiveInterval().getSeconds());
this.delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime().toEpochMilli());
this.isNew = true;
this.flushImmediateIfNecessary();

在 flushImmediateIfNecessary 方法中,如果 redisFlushMode 是 IMMEDIATE 模式,則會立即保存 session 進 redis ,但默認配置的是 ON_SAVE ,那是在哪里保存進 redis 的呢,我們回到最開始的過濾器 doFilterInternal 方法中,在 finally 中有一句

wrappedRequest.commitSession();

就是在這里將 session 存儲進 redis 的 ,我們跟進去看看,核心語句為這句

SessionRepositoryFilter.this.sessionRepository.save(session);
session.saveDelta();
if (session.isNew()) {
  String sessionCreatedKey = getSessionCreatedChannel(session.getId());
  this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
  session.setNew(false);
}

進入 saveDelta ,在這里進行了 hash 結構的設置

getSessionBoundHashOperations(sessionId).putAll(this.delta);

最后一行進行了過期時間的設置和把當前 key 加入 set ,讀者自行查看

RedisOperationsSessionRepository.this.expirationPolicy
          .onExpirationUpdated(originalExpiration, this);

修改一些參數(shù)

實際業(yè)務中,可能需要修改一些參數(shù)才能達到我們業(yè)務的需求,最常見的需求就是修改 session 的過期時間了,在 EnableRedisHttpSession 注解中,已經(jīng)提供了一些基本的配置如

maxInactiveIntervalInSeconds 最大過期時間,默認 30 分鐘
redisNamespace 插入到 redis 的 session 命名空間,默認是 spring:session
cleanupCron 過期 session 清理任務,默認是 1 分鐘清理一次
redisFlushMode 刷新方式 ,其實在上面原理的 flushImmediateIfNecessary 方法中有用到,默認是 ON_SAVE
redisNamespace 是一定要修改的,這個不修改會影響別的項目,一般使用我們項目的名稱加關鍵字 session 做 key ,表明這是這個項目的 session 信息。

不過這樣的配置明顯不夠,對于最大過期時間來說,有可能需要加到配置文件中去,而不是寫在代碼中,但是這里沒有提供占位符的功能,回到 RedisOperationsSessionRepository 的創(chuàng)建,最終配置的 maxInactiveIntervalInSeconds 還是要設置到這個 bean 中去的,我們可以把這個 bean 的創(chuàng)建過程覆蓋,重寫 maxInactiveIntervalInSeconds 的獲取過程,就解決了,代碼如下

@Autowired
RedisTemplate sessionRedisTemplate;

@Autowired
ApplicationEventPublisher applicationEventPublisher;

@Value("${server.session.timeout}")
private int sessionTimeout = 1800;

@Primary    // 使用 Primary 來覆蓋默認的 Bean 
@Bean
public RedisOperationsSessionRepository sessionRepository() {
  RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(sessionRedisTemplate);
  // 這里要把原來的屬性引用過來,避免出錯 ,可以引用原來的類并復制屬性 ;像 redisNamespace,redisFlushMode 都要復制過來
  return sessionRepository;
}

還有一個就是 redis 的序列化問題,默認是使用的 jdk 的對象序列化,很容易出現(xiàn)加一個字段或減少一個字段出現(xiàn)不能反序列化,所以序列化方式是需要換的,如果項目中的緩存就已經(jīng)使用了對象序列化的話,那就面要為其單獨寫一個 redisTemplate 并設置進去,在構建 RedisOperationsSessionRepository 的時候設置 redisTemplate

還有一個就是生成在 redis 中的 key 值都是 uuid 的形式,根本沒辦法知道當前這個 key 是哪個用戶在哪里登錄的,我們其實可以修改它的 key 為 userId_ip_time 的形式,用來表明這個用戶什么時間在哪個 ip 有登錄過,我是這么玩的(沒有在實際中使用過,雖然能改,但可能有坑):

經(jīng)過前面的源碼分析,創(chuàng)建 session 并保存到 redis 的是 RedisOperationsSessionRepository 的 createSession 方法,但是這里寫死了 RedisSession 使用空的構造,而且 RedisSession 是 final 的內(nèi)部類,訪問權限為默認,構造的時候 new MapSession 也是默認的,最終那個 id 為使用 UUID ,看起來一點辦法都沒有,其實在這里創(chuàng)建完 session ,用戶不一定是登錄成功的狀態(tài),我們應該在登錄成功才能修改 session 的 key ,好在 RedisOperationsSessionRepository 提供了一個方法 findById ,我們可以在這個上面做文章,先把 RedisSession 查出來,然后用反射得到 MapSession ,然后留意到 MapSession 是可以修改 id 的,它自己也提供了方法 changeSessionId ,我們完全可以在登錄成功調(diào)用 setId 修改 sessionId ,然后再寫回去,這個代碼一定要和 RedisSession 在同包 代碼如下:

package org.springframework.session.data.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.MapSession;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

@Component
public class SessionOperation {
  @Autowired
  private RedisOperationsSessionRepository redisOperationsSessionRepository;

  public void loginSuccess(String userId){
    String sessionId = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession().getId();
    RedisOperationsSessionRepository.RedisSession redisSession = redisOperationsSessionRepository.findById(sessionId);
    Field cached = ReflectionUtils.findField(RedisOperationsSessionRepository.RedisSession.class, "cached");
    ReflectionUtils.makeAccessible(cached);
    MapSession mapSession = (MapSession) ReflectionUtils.getField(cached, redisSession);
    mapSession.setId("userId:1");
    redisOperationsSessionRepository.save(redisSession);
  }
}

源碼地址: https://gitee.com/sanri/example/tree/master/test-redis-session

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • SpringBoot整合JWT框架,解決Token跨域驗證問題

    SpringBoot整合JWT框架,解決Token跨域驗證問題

    Json web token (JWT), 是為了在網(wǎng)絡應用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標準((RFC 7519).定義了一種簡潔的,自包含的方法用于通信雙方之間以JSON對象的形式安全的傳遞信息。
    2021-06-06
  • 基于SpringAI+DeepSeek實現(xiàn)流式對話功能

    基于SpringAI+DeepSeek實現(xiàn)流式對話功能

    一般來說大模型的響應速度通常是很慢的,為了避免用戶用戶能夠耐心等待輸出的結果,我們通常會使用流式輸出一點點將結果輸出給用戶,那么問題來了,想要實現(xiàn)流式結果輸出,后端和前端要如何配合?下來本文給出具體的實現(xiàn)代碼,需要的朋友可以參考下
    2025-02-02
  • Springboot導出文件,前端下載文件方式

    Springboot導出文件,前端下載文件方式

    這篇文章主要介紹了Springboot導出文件,前端下載文件方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java 5億整數(shù)大文件怎么排序

    Java 5億整數(shù)大文件怎么排序

    這篇文章主要介紹了Java 5億整數(shù)大文件怎么排序,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03
  • spring boot實現(xiàn)圖片上傳和下載功能

    spring boot實現(xiàn)圖片上傳和下載功能

    這篇文章主要為大家詳細介紹了spring boot實現(xiàn)圖片上傳和下載功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Spring?Boot?利用?XML?方式整合?MyBatis

    Spring?Boot?利用?XML?方式整合?MyBatis

    這篇文章主要介紹了Spring?Boot?利用?XML?方式整合?MyBatis,文章圍繞主題的相關資料展開詳細的內(nèi)容介紹,具有一定的參考價值,組要的小伙伴可以參考一下
    2022-05-05
  • 通過Java實現(xiàn)設置Word文檔頁邊距的方法詳解

    通過Java實現(xiàn)設置Word文檔頁邊距的方法詳解

    頁邊距是指頁面的邊線到文字的距離。通常可在頁邊距內(nèi)部的可打印區(qū)域中插入文字和圖形等。今天這篇文章將為您展示如何通過編程方式,設置Word?文檔頁邊距,感興趣的可以了解一下
    2023-02-02
  • java字符串所有操作方法匯總

    java字符串所有操作方法匯總

    這篇文章主要介紹了java字符串所有操作方法匯總,需要的朋友可以參考下,在實際的編程中,我們經(jīng)常需要對字符串進行各種操作,例如連接、截取、替換等,本文將按類別介紹一些Java字符串的常用方法,幫助讀者更好地理解和使用
    2023-11-11
  • SpringBoot淺析安全管理之OAuth2框架

    SpringBoot淺析安全管理之OAuth2框架

    安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會出現(xiàn)問題,這篇文章主要介紹了SpringBoot安全管理OAuth2框架的使用
    2022-08-08
  • Java經(jīng)典排序算法之插入排序

    Java經(jīng)典排序算法之插入排序

    這篇文章主要為大家詳細介紹了Java經(jīng)典排序算法之插入排序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04

最新評論

江永县| 甘德县| 马关县| 织金县| 潼南县| 云龙县| 青铜峡市| 长岭县| 垫江县| 恩平市| 军事| 南康市| 札达县| 大田县| 沭阳县| 西城区| 章丘市| 福贡县| 无锡市| 吉首市| 荃湾区| 长岛县| 长宁县| 襄城县| 雅安市| 临泉县| 曲周县| 象山县| 沙田区| 莱阳市| 龙陵县| 改则县| 监利县| 子长县| 从化市| 淮南市| 东阳市| 彭水| 怀安县| 定西市| 自贡市|