Redis快速實現分佈式session的方法詳解
前言
我們在開發一個項目時通常需要登錄認證,常用的登錄認證技術實現框架有Spring Security和shiro
Spring Security
Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架。它是保護基於spring的應用程序的事實上的標準。
Spring Security是一個專註於為Java應用程序提供身份驗證和授權的框架。與所有Spring項目一樣,Spring Security的真正強大之處在於它可以很容易地擴展以滿足定制需求,並且Spring Security和spring更加適配貼合,我們工作中常常使用到Spring Security。
Apache Shiro
Apache Shiro 是 Java 的一個安全框架。目前,使用 Apache Shiro 的人越來越多,因為它相當簡單,對比Spring Security,可能沒有 Spring Security 做的功能強大,但是在實際工作時可能並不需要那麼復雜的東西,所以使用小而簡單的 Shiro 就足夠瞭。
不足:
這些都是認證技術框架,在單體應用中都是常用的技術框架,但是在分佈式中,應用可能要部署多份,這時通過nginx分發請求,但是每個單體應用都要可能重復驗證,因為他們的seesion
數據是放在他們自己服務中的。
Session作用
Session是客戶端與服務器通訊會話跟蹤技術,服務器與客戶端保持整個通訊的會話基本信息。
客戶端在第一次訪問服務端的時候,服務端會響應一個sessionId並且將它存入到本地cookie中,在之後的訪問會將cookie中的sessionId放入到請求頭中去訪問服務器。
spring-session
Spring Session是Spring的項目之一,Spring Session把servlet容器實現的httpSession替換為spring-session,專註於解決session管理問題。
Spring Session提供瞭集群Session(Clustered Sessions)功能,默認采用外置的Redis來存儲Session數據,以此來解決Session共享的問題。
spring-session提供對用戶session管理的一系列api和實現。提供瞭很多可擴展、透明的封裝方式用於管理httpSession/WebSocket的處理。
支持功能
- 輕易把session存儲到第三方存儲容器,框架提供瞭redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多種存儲session的容器的方式。這樣可以獨立於應用服務器的方式提供高質量的集群。
- 同一個瀏覽器同一個網站,支持多個session問題。 從而能夠很容易地構建更加豐富的終端用戶體驗。
- Restful API,不依賴於cookie。可通過header來傳遞jessionID 。控制session id如何在客戶端和服務器之間進行交換,這樣的話就能很容易地編寫Restful API,因為它可以從HTTP 頭信息中獲取session id,而不必再依賴於cookie
- WebSocket和spring-session結合,同步生命周期管理。當用戶使用WebSocket發送請求的時候
分佈式seesion實戰
步驟1:依賴包
因為是web應用。我們加入springboot的常用依賴包web,加入SpringSession、redis的依賴包,移支持把session存儲在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>
這裡因為是把seesion存儲在redis,這樣每個服務登錄都是去查看redis中數據進行驗證的,所有是分佈式的。 這裡要引入spring-session-data-redis和spring-boot-starter-redis
步驟2:配置文件
spring.application.name=spring-boot-redis server.port=9090 # 設置session的存儲方式,采用redis存儲 spring.session.store-type=redis # session有效時長為15分鐘 server.servlet.session.timeout=PT15M ## Redis 配置 ## Redis數據庫索引 spring.redis.database=1 ## Redis服務器地址 spring.redis.host=127.0.0.1 ## Redis服務器連接端口 spring.redis.port=6379 ## Redis服務器連接密碼(默認為空) spring.redis.password=
步驟3:實現邏輯
初始化用戶數據
@Slf4j @RestController @RequestMapping(value = "/user") public class UserController { Map<String, User> userMap = new HashMap<>(); public UserController() { //初始化1個用戶,用於模擬登錄 User u1=new User(1,"user1","user1"); userMap.put("user1",u1); } }
這裡就不用使用數據庫瞭,初始化兩條數據代替數據庫,用於模擬登錄
登錄
@GetMapping(value = "/login") public String login(String username, String password, HttpSession session) { //模擬數據庫的查找 User user = this.userMap.get(username); if (user != null) { if (!password.equals(user.getPassword())) { return "用戶名或密碼錯誤!!!"; } else { session.setAttribute(session.getId(), user); log.info("登錄成功{}",user); } } else { return "用戶名或密碼錯誤!!!"; } return "登錄成功!!!"; }
登錄接口,根據用戶名和密碼登錄,這裡進行驗證,如果驗證登錄成功,使用 session.setAttribute(session.getId(), user);把相關信息放到session中。
查找用戶
/** * 通過用戶名查找用戶 */ @GetMapping(value = "/find/{username}") public User find(@PathVariable String username) { User user=this.userMap.get(username); log.info("通過用戶名={},查找出用戶{}",username,user); return user; }
模擬通過用戶名查找用戶
獲取session
/** *拿當前用戶的session */ @GetMapping(value = "/session") public String session(HttpSession session) { log.info("當前用戶的session={}",session.getId()); return session.getId(); }
退出登錄
/** * 退出登錄 */ @GetMapping(value = "/logout") public String logout(HttpSession session) { log.info("退出登錄session={}",session.getId()); session.removeAttribute(session.getId()); return "成功退出!!"; }
這裡退出時,要把session中的用戶信息刪除。
步驟4:編寫session攔截器
session攔截器的作用:驗證當前用戶發來的請求是否有攜帶sessionid,如果沒有攜帶,提示用戶重新登錄。
@Configuration public class SecurityInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { HttpSession session = request.getSession(); //驗證當前session是否存在,存在返回true true代表能正常處理業務邏輯 if (session.getAttribute(session.getId()) != null){ log.info("session攔截器,session={},驗證通過",session.getId()); return true; } //session不存在,返回false,並提示請重新登錄。 response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.getWriter().write("請登錄!!!!!"); log.info("session攔截器,session={},驗證失敗",session.getId()); return false; } }
步驟5:把攔截器註入到攔截器鏈中
@Slf4j @Configuration public class SessionCofig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SecurityInterceptor()) //排除攔截的2個路徑 .excludePathPatterns("/user/login") .excludePathPatterns("/user/logout") //攔截所有URL路徑 .addPathPatterns("/**"); } }
步驟6:測試
登錄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中數據:
seesion數據已經保存到redis瞭,到這裡我們就整合瞭使用spring-seesion實現分佈式seesion功能。
以上就是Redis快速實現分佈式session的方法詳解的詳細內容,更多關於Redis 分佈式session的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- SpringBoot 整合 Spring-Session 實現分佈式會話項目實戰
- springboot使用Redis作緩存使用入門教程
- 解決shiro 定時監聽器不生效的問題 onExpiration不調用問題
- Redis實現Session共享與單點登錄
- SpringBoot詳解如何整合Redis緩存驗證碼