Redis快速實(shí)現(xiàn)分布式session的方法詳解
前言
我們?cè)陂_發(fā)一個(gè)項(xiàng)目時(shí)通常需要登錄認(rèn)證,常用的登錄認(rèn)證技術(shù)實(shí)現(xiàn)框架有Spring Security和shiro
Spring Security
Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架。它是保護(hù)基于spring的應(yīng)用程序的事實(shí)上的標(biāo)準(zhǔn)。
Spring Security是一個(gè)專注于為Java應(yīng)用程序提供身份驗(yàn)證和授權(quán)的框架。與所有Spring項(xiàng)目一樣,Spring Security的真正強(qiáng)大之處在于它可以很容易地?cái)U(kuò)展以滿足定制需求,并且Spring Security和spring更加適配貼合,我們工作中常常使用到Spring Security。
Apache Shiro
Apache Shiro 是 Java 的一個(gè)安全框架。目前,使用 Apache Shiro 的人越來越多,因?yàn)樗喈?dāng)簡(jiǎn)單,對(duì)比Spring Security,可能沒有 Spring Security 做的功能強(qiáng)大,但是在實(shí)際工作時(shí)可能并不需要那么復(fù)雜的東西,所以使用小而簡(jiǎn)單的 Shiro 就足夠了。
不足:
這些都是認(rèn)證技術(shù)框架,在單體應(yīng)用中都是常用的技術(shù)框架,但是在分布式中,應(yīng)用可能要部署多份,這時(shí)通過nginx分發(fā)請(qǐng)求,但是每個(gè)單體應(yīng)用都要可能重復(fù)驗(yàn)證,因?yàn)樗麄兊?code>seesion數(shù)據(jù)是放在他們自己服務(wù)中的。
Session作用
Session是客戶端與服務(wù)器通訊會(huì)話跟蹤技術(shù),服務(wù)器與客戶端保持整個(gè)通訊的會(huì)話基本信息。
客戶端在第一次訪問服務(wù)端的時(shí)候,服務(wù)端會(huì)響應(yīng)一個(gè)sessionId并且將它存入到本地cookie中,在之后的訪問會(huì)將cookie中的sessionId放入到請(qǐng)求頭中去訪問服務(wù)器。
spring-session
Spring Session是Spring的項(xiàng)目之一,Spring Session把servlet容器實(shí)現(xiàn)的httpSession替換為spring-session,專注于解決session管理問題。
Spring Session提供了集群Session(Clustered Sessions)功能,默認(rèn)采用外置的Redis來存儲(chǔ)Session數(shù)據(jù),以此來解決Session共享的問題。
spring-session提供對(duì)用戶session管理的一系列api和實(shí)現(xiàn)。提供了很多可擴(kuò)展、透明的封裝方式用于管理httpSession/WebSocket的處理。
支持功能
- 輕易把session存儲(chǔ)到第三方存儲(chǔ)容器,框架提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多種存儲(chǔ)session的容器的方式。這樣可以獨(dú)立于應(yīng)用服務(wù)器的方式提供高質(zhì)量的集群。
- 同一個(gè)瀏覽器同一個(gè)網(wǎng)站,支持多個(gè)session問題。 從而能夠很容易地構(gòu)建更加豐富的終端用戶體驗(yàn)。
- Restful API,不依賴于cookie??赏ㄟ^header來傳遞jessionID ??刂苨ession id如何在客戶端和服務(wù)器之間進(jìn)行交換,這樣的話就能很容易地編寫Restful API,因?yàn)樗梢詮腍TTP 頭信息中獲取session id,而不必再依賴于cookie
- WebSocket和spring-session結(jié)合,同步生命周期管理。當(dāng)用戶使用WebSocket發(fā)送請(qǐng)求的時(shí)候
分布式seesion實(shí)戰(zhàn)
步驟1:依賴包
因?yàn)槭莣eb應(yīng)用。我們加入springboot的常用依賴包web,加入SpringSession、redis的依賴包,移支持把session存儲(chǔ)在redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>這里因?yàn)槭前裺eesion存儲(chǔ)在redis,這樣每個(gè)服務(wù)登錄都是去查看redis中數(shù)據(jù)進(jìn)行驗(yàn)證的,所有是分布式的。 這里要引入spring-session-data-redis和spring-boot-starter-redis
步驟2:配置文件
spring.application.name=spring-boot-redis server.port=9090 # 設(shè)置session的存儲(chǔ)方式,采用redis存儲(chǔ) spring.session.store-type=redis # session有效時(shí)長(zhǎng)為15分鐘 server.servlet.session.timeout=PT15M ## Redis 配置 ## Redis數(shù)據(jù)庫(kù)索引 spring.redis.database=1 ## Redis服務(wù)器地址 spring.redis.host=127.0.0.1 ## Redis服務(wù)器連接端口 spring.redis.port=6379 ## Redis服務(wù)器連接密碼(默認(rèn)為空) spring.redis.password=
步驟3:實(shí)現(xiàn)邏輯
初始化用戶數(shù)據(jù)
@Slf4j
@RestController
@RequestMapping(value = "/user")
public class UserController {
Map<String, User> userMap = new HashMap<>();
public UserController() {
//初始化1個(gè)用戶,用于模擬登錄
User u1=new User(1,"user1","user1");
userMap.put("user1",u1);
}
}這里就不用使用數(shù)據(jù)庫(kù)了,初始化兩條數(shù)據(jù)代替數(shù)據(jù)庫(kù),用于模擬登錄
登錄
@GetMapping(value = "/login")
public String login(String username, String password, HttpSession session) {
//模擬數(shù)據(jù)庫(kù)的查找
User user = this.userMap.get(username);
if (user != null) {
if (!password.equals(user.getPassword())) {
return "用戶名或密碼錯(cuò)誤?。?!";
} else {
session.setAttribute(session.getId(), user);
log.info("登錄成功{}",user);
}
} else {
return "用戶名或密碼錯(cuò)誤?。?!";
}
return "登錄成功?。?!";
}登錄接口,根據(jù)用戶名和密碼登錄,這里進(jìn)行驗(yàn)證,如果驗(yàn)證登錄成功,使用 session.setAttribute(session.getId(), user);把相關(guān)信息放到session中。
查找用戶
/**
* 通過用戶名查找用戶
*/
@GetMapping(value = "/find/{username}")
public User find(@PathVariable String username) {
User user=this.userMap.get(username);
log.info("通過用戶名={},查找出用戶{}",username,user);
return user;
}模擬通過用戶名查找用戶
獲取session
/**
*拿當(dāng)前用戶的session
*/
@GetMapping(value = "/session")
public String session(HttpSession session) {
log.info("當(dāng)前用戶的session={}",session.getId());
return session.getId();
}退出登錄
/**
* 退出登錄
*/
@GetMapping(value = "/logout")
public String logout(HttpSession session) {
log.info("退出登錄session={}",session.getId());
session.removeAttribute(session.getId());
return "成功退出?。?;
}這里退出時(shí),要把session中的用戶信息刪除。
步驟4:編寫session攔截器
session攔截器的作用:驗(yàn)證當(dāng)前用戶發(fā)來的請(qǐng)求是否有攜帶sessionid,如果沒有攜帶,提示用戶重新登錄。
@Configuration
public class SecurityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpSession session = request.getSession();
//驗(yàn)證當(dāng)前session是否存在,存在返回true true代表能正常處理業(yè)務(wù)邏輯
if (session.getAttribute(session.getId()) != null){
log.info("session攔截器,session={},驗(yàn)證通過",session.getId());
return true;
}
//session不存在,返回false,并提示請(qǐng)重新登錄。
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.getWriter().write("請(qǐng)登錄?。。。?!");
log.info("session攔截器,session={},驗(yàn)證失敗",session.getId());
return false;
}
}步驟5:把攔截器注入到攔截器鏈中
@Slf4j
@Configuration
public class SessionCofig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SecurityInterceptor())
//排除攔截的2個(gè)路徑
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/logout")
//攔截所有URL路徑
.addPathPatterns("/**");
}
}步驟6:測(cè)試
登錄user1用戶:http://127.0.0.1:9090/user/login?username=user1&password=user1
查詢user1用戶session:http://127.0.0.1:9090/user/session
退出登錄: http://127.0.0.1:9090/user/logout
登錄后查看redis中數(shù)據(jù):

seesion數(shù)據(jù)已經(jīng)保存到redis了,到這里我們就整合了使用spring-seesion實(shí)現(xiàn)分布式seesion功能。
以上就是Redis快速實(shí)現(xiàn)分布式session的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Redis 分布式session的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Redis上實(shí)現(xiàn)分布式鎖以提高性能的方案研究
這篇文章主要介紹了Redis上實(shí)現(xiàn)分布式鎖以提高性能的方案研究,其中重點(diǎn)需要理解異步算法與鎖的自動(dòng)釋放,需要的朋友可以參考下2015-12-12
如何通過redis減庫(kù)存的秒殺場(chǎng)景實(shí)現(xiàn)
本文通過解決秒殺系統(tǒng)中的一個(gè)場(chǎng)景即數(shù)據(jù)預(yù)加載,即把庫(kù)存數(shù)據(jù)事先加載到緩存,然后通過緩存來更新庫(kù)存,簡(jiǎn)單介紹了如何通過redis減庫(kù)存的秒殺場(chǎng)景實(shí)現(xiàn),感興趣的可以了解一下2022-06-06
Redis優(yōu)惠券秒殺企業(yè)實(shí)戰(zhàn)
本文主要介紹了Redis優(yōu)惠券秒殺企業(yè)實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
redis查詢keys報(bào)錯(cuò)的實(shí)現(xiàn)
在Redis中使用KEYS命令來查詢所有符合特定模式的鍵名是一個(gè)常見需求,本文主要介紹了redis查詢keys報(bào)錯(cuò)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2025-04-04
Redis 對(duì)比 Memcached 并在 CentOS 下進(jìn)行安裝配置詳解
Redis 是一個(gè)開源、支持網(wǎng)絡(luò)、基于內(nèi)存、鍵值對(duì)的 Key-Value 數(shù)據(jù)庫(kù),本篇文章主要介紹了Redis 對(duì)比 Memcached 并在 CentOS 下進(jìn)行安裝配置詳解,有興趣的可以了解一下。2016-11-11
解讀Redis秒殺優(yōu)化方案(阻塞隊(duì)列+基于Stream流的消息隊(duì)列)
該文章介紹了使用Redis的阻塞隊(duì)列和Stream流的消息隊(duì)列來優(yōu)化秒殺系統(tǒng)的方案,通過將秒殺流程拆分為兩條流水線,使用Redis緩存緩解數(shù)據(jù)庫(kù)壓力,并結(jié)合Lua腳本進(jìn)行原子性判斷,使用阻塞隊(duì)列和消息隊(duì)列異步處理訂單,有效提高了系統(tǒng)的并發(fā)處理能力和可用性2025-02-02
淺析Redis底層數(shù)據(jù)結(jié)構(gòu)Dict
Redis是一個(gè)鍵值型的數(shù)據(jù)庫(kù),我們可以根據(jù)鍵實(shí)現(xiàn)快速的增刪改查,而鍵與值的映射關(guān)系正是通過Dict來實(shí)現(xiàn)的,當(dāng)然?Dict?也是?Set?Hash?的實(shí)現(xiàn)方式,本文就詳細(xì)帶大家介紹一下Redis底層數(shù)據(jù)結(jié)構(gòu)?Dict,,需要的朋友可以參考下2023-05-05

