在之前的幾章裏面,我們分別做了快速入門、自定義表單登錄、自定義手機登錄。他們有一個共同點,就是目前我們與客戶端之間的交互都依賴於 Session。那麼本章我們就帶大家來了解一下 SpringSecurity 下 Session 的使用與設置。
Session 是什麼?
Session 中文意思爲會議,在計算機中,尤其在網絡應用中稱爲會話控制。
Session直接翻譯成中文比較困難,一般都譯成時域。在計算機專業術語中,Session是指一個終端用戶與交互系統進行通信的時間間隔,通常指從註冊進入系統到註銷退出系統之間所經過的時間。以及如果需要的話,可能還有一定的操作空間。
百度百科
爲什麼要使用 Session?
Http 協議本身是無狀態的,多次連接之間的狀態無法共享,服務端就判斷是否是同一個用戶操作的就比較麻煩。
而 Session 就是用來保存狀態的,允許通過將對象存儲在 Web 服務器內存中,在整個用戶會話過程中進行共享。
Session 原理
瀏覽器第一次訪問服務器時,服務端會生成 Session,然後同時爲該 Session 生成一個唯一的 SessionId,然後將 SessionId 與 Session 以鍵值對的形式存儲到內存中(Java 默認)。將 SessionId 以 Cookie 的方式返回給瀏覽器。瀏覽器下一次訪問的時候就攜帶 SessionId 來訪問,這樣服務端通過查詢就可以取出 Session 達到共享了。
在 SpringSecurity 裏面的使用
單機 Session
-
Session 超時設置
-
設置 Session 超時時間,在系統配置文件(application.properties/yml) 中配置
server: session: timeout: 600 #單位爲 Second,設置的時間低於 1 分鐘,按 1 分鐘處理
-
設置 Session 超時/失效後繼續訪問處理,可以設置跳轉頁面,也可以設置跳轉接口。前後端分離一般返回 Json,不分離可以引導頁面跳轉。兩者都要兼容,可以跳轉到自定義接口判斷實際請求,進行處理。
// 設置 Session 失效後訪問跳轉的頁面 http.sessionManagement().invalidSessionUrl("/session/invalid");
-
-
Session 併發控制
-
Session 最大數設置,限制系統中用戶數量
http.sessionManagement().maximumSessions(1)// 設置同時在線的最大 Session 數
-
設置 Session 數量達到最大值時,是否阻止後面用戶登錄
// 設置當 Session 數量達到最大數量時,阻止後面的登錄 http.sessionManagement().maxSessionsPreventsLogin(true);
-
自定義設置 Session 數量達到最大時後續登錄的處理策略,默認實現會將前面用戶的 Session 置爲失效
// 設置 Session 併發登錄時處理策略 http.sessionManagement().expiredSessionStrategy(new DefaultExpiredSessionStrategy());
/** * @author: hblolj * @Date: 2019/3/16 9:13 * @Description: Session 併發登錄時的處理策略 * @Version: **/ public class DefaultExpiredSessionStrategy implements SessionInformationExpiredStrategy{ @Override public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { event.getResponse().setCharacterEncoding("UTF-8"); event.getResponse().getWriter().write("併發登錄!"); } }
-
集羣 Session(單機 Session 下的設置在集羣下一樣有效)
在單機 Session 下基本上只要處理好上面的問題就可以了,但是隨着系統越來越大,單體應用可能滿足不了我們的需求。如果我們對應用進行了集羣,那麼用戶在每個應用上都會產生一個 Session,並且這些 Session 還不是共享的,就達不到 Session 使用的初衷了。
爲了讓 Session 可以共享,一般我們就會改變 Session 的存儲方式,Session 默認存儲在內存中,多個主機間就不方便共享。主流處理方案有:
- 保存在數據庫
- 保存在 Redis 中
- 利用 Servlet 容器來進行 Session 同步
- 或者使用 Nginx 使用用戶 ip 進行哈希處理,在負載均衡時,讓用戶每次訪問同一個主機避免多 Session 問題
我們這裏主要還是討論 SpringSecurity 下集羣 Session 的處理方案,其他擴展信息,大家感興趣可以自行查詢,或者留言討論。
我們這裏示例使用 Redis 作爲 Session 存儲方式,通過下列配置 Session 就會默認存儲在 Redis 中了。關於 SpringBoot 下 Redis 的使用,不瞭解的可以查詢資料使用,上手還是比較簡單的。
-
使用 Redis 作爲 Session 的存儲,通過配置文件指定存儲方式。
spring: session: store-type: redis #設置將 Session 存儲在 Redis 中 redis: host: redis 所在的主機 port: 默認是 6379 database: 選擇使用的 database password: your self password pool: max-wait: -1 max-idle: 1 max-active: 8 min-idle: 0
-
添加 Redis 依賴
<!-- SpringBoot 版本 1.5.19.RELEASE--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
添加 Spring-Session 依賴
<!-- SpringBoot 版本 1.5.19.RELEASE--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> </dependency>
-
除了 Redis,SpringBoot 還支持一下存儲方式,大家可以自行擴展使用
public enum StoreType { REDIS, MONGO, JDBC, HAZELCAST, HASH_MAP, NONE; private StoreType() { } }
退出登錄
既然 Session 中共享用戶多次請求的狀態,那麼如果我們要用戶登出,就只要清空 Session 即可。那麼在 SpringSecurity 下是怎樣設置的呢?
-
如何退出
-
請求登出地址,默認爲 /logout
-
自定義登出地址
http.logout().logoutUrl("/quit");// 默認是 logout
-
-
退出處理原理 (訪問退出地址,退出處理邏輯 SpringSecurity 有默認實現)
-
使當前 Session 失效
(HttpSession)session.invalidate();
-
清除與當前用戶先關的 remember-me 記錄
if (authentication != null) { this.tokenRepository.removeUserTokens(authentication.getName()); }
-
清空當前的 SecurityContext
if (this.clearAuthentication) { SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication((Authentication)null); } SecurityContextHolder.clearContext();
-
-
與退出相關的配置
-
自定義退出成功跳轉頁面或處理策略,同時設置只有 logoutSuccessHandler 會生效
http .logout() .logoutSuccessUrl("http://www.baidu.com") // 設置退出成功跳轉的頁面 .logoutSuccessHandler(new DefaultLogoutSuccessHandler()) // 設置退出成功的處理策略
/** * @author: hblolj * @Date: 2019/3/16 10:28 * @Description: * @Version: **/ @Slf4j public class DefaultLogoutSuccessHandler implements LogoutSuccessHandler{ @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { log.info("Logout Success!"); } }
-
設置退出時刪除客戶端 Cookie
http.logout().deleteCookies("JSESSIONID") // 退出時刪除客戶端指定的 Cookie
-
OK,到了這裏,基本上在 SpringSecurity 裏面 Session 的常規設置與使用都介紹完了。大家如果有什麼問題,歡迎在下面的留言區留言討論。
實際上,在現在的開發中,單體 Web 應用可能還是以 Session 爲主,但是如果要同時支持 App 的話, Session 就不是那麼好用了,因爲 Session 依賴 Cookie,雖然 App 訪問時可以模擬瀏覽器設置 Cookie,但是契合度還是不如 Web,所以對於 App 或者集羣環境下,現在主流使用基於 OAuth2 的 Token 作爲驗證。是不是以爲我們下一章要講 SpringSecurity 下 OAuth2 協議的集成與 Token 使用呢?哈哈,下一章我們計劃帶大家集成一下 SpringSecurity 下第三方登錄,比如 QQ 快速登錄、微信快速登錄等(也依賴於 OAuth 協議)。至於集成 Token 認證還要往後幾章哦!
To Be Continue!