本文做者:徐靖峯
原文連接:http://t.cn/Rp7xUro
版權歸做者全部,轉載請註明出處html
以前項目須要使用Redis管理Session,找了不少的Demo,研究了一天Spring Session源碼,感受有點難繼續。忽然看到羣裏Pivotal的周輝大牛發了一篇鏈接,忽然豁然開朗。html5
Session和Cookie這兩個概念,在學習java web開發之初,大多數人就已經接觸過了。最近在研究跨域單點登陸的實現時,發現對於Session和Cookie的瞭解,並非很深刻,因此打算寫兩篇文章記錄一下本身的理解。在咱們的應用集成Spring Session以前,先補充一點Session和Cookie的關鍵知識。java
因爲http協議是無狀態的協議,爲了可以記住請求的狀態,因而引入了Session和Cookie的機制。咱們應該有一個很明確的概念,那就是Session是存在於服務器端的,在單體式應用中,他是由tomcat管理的,存在於tomcat的內存中,當咱們爲了解決分佈式場景中的session共享問題時,引入了redis,其共享內存,以及支持key自動過時的特性,很是契合session的特性,咱們在企業開發中最經常使用的也就是這種模式。可是隻要你願意,也能夠選擇存儲在JDBC,Mongo中,這些,spring都提供了默認的實現,在大多數狀況下,咱們只須要引入配置便可。而Cookie則是存在於客戶端,更方便理解的說法,能夠說存在於瀏覽器。Cookie並不經常使用,至少在我不長的web開發生涯中,並無什麼場景須要我過多的關注Cookie。http協議容許從服務器返回Response時攜帶一些Cookie,而且同一個域下對Cookie的數量有所限制,以前說過Session的持久化依賴於服務端的策略,而Cookie的持久化則是依賴於本地文件。雖說Cookie並不經常使用,可是有一類特殊的Cookie倒是咱們須要額外關注的,那即是與Session相關的sessionId,他是真正維繫客戶端和服務端的橋樑。web
用戶發起請求,服務器響應請求,並作一些用戶信息的處理,隨後返回響應給用戶;用戶再次發起請求,攜帶sessionId,服務器便可以識別,這個用戶就是以前請求的那個。redis
使用Springboot編寫一個很是簡單的服務端,來加深對其的理解。需求很簡單,當瀏覽器訪問localhost:8080/test/cookie?browser=xxx
時,若是沒有獲取到session,則將request中的browser存入session;若是獲取到session,便將session中的browser值輸出。順便將request中的全部cookie打印出來。spring
@Controller public class CookieController { @RequestMapping("/test/cookie") public String cookie(@RequestParam("browser") String browser, HttpServletRequest request, HttpSession session) { //取出session中的browser Object sessionBrowser = session.getAttribute("browser"); if (sessionBrowser == null) { System.out.println("不存在session,設置browser=" + browser); session.setAttribute("browser", browser); } else { System.out.println("存在session,browser=" + sessionBrowser.toString()); } Cookie[] cookies = request.getCookies(); if (cookies != null && cookies.length > 0) { for (Cookie cookie : cookies) { System.out.println(cookie.getName() + " : " + cookie.getValue()); } } return "index"; } }
咱們沒有引入其餘任何依賴,看看原生的session機制是什麼。chrome
1 使用chrome瀏覽器,訪問localhost:8080/test/cookie?browser=chrome
,控制檯輸出以下:後端
1api |
Session Info: 不存在session,設置browser=chrome跨域 |
既沒有session,也沒有cookie,而且根據咱們將browser=chrome已經設置到了session中。
再次訪問一樣的地址,控制檯輸出以下:
1 2 |
Session Info: 存在session,browser=chrome Cookie Info: JSESSIONID : 4CD1D96E04FC390EA6C60E8C40A636AF |
屢次訪問以後,控制檯依舊打印出一樣的信息。
稍微解讀下這個現象,能夠驗證一些結論。當服務端往session中保存一些數據時,Response中自動添加了一個Cookie:JSESSIONID:xxxx,再後續的請求中,瀏覽器也是自動的帶上了這個Cookie,服務端根據Cookie中的JSESSIONID取到了對應的session。這驗證了一開始的說法,客戶端服務端是經過JSESSIONID進行交互的,而且,添加和攜帶key爲JSESSIONID的Cookie都是tomcat和瀏覽器自動幫助咱們完成的,這很關鍵。
2 使用360瀏覽器,訪問localhost:8080/test/cookie?browser=360
第一次訪問:
1 |
Session Info: 不存在session,設置browser=360 |
後續訪問:
1 2 |
Session Info: 存在session,browser=360 Cookie Info: JSESSIONID : 320C21A645A160C4843D076204DA2F40 |
爲何要再次使用另外一個瀏覽器訪問呢?先賣個關子,咱們最起碼能夠得出結論,不一樣瀏覽器,訪問是隔離的,甚至從新打開同一個瀏覽器,JSESSIONID也是不一樣的。
其實上述的知識點,都是很是淺顯的,之因此囉嗦一句,是爲了引出這一節的內容,以及方便觀察後續咱們引入Spring Session以後的發生的變化。
還記得上一節的代碼示例中,咱們使用了兩個瀏覽器:
咱們使用chrome插件Edit this Cookie,將chrome瀏覽器中的JSESSIONID修改成360瀏覽器中的值
一樣訪問原來的端點:localhost:8080/test/cookie?browser=chrome
,獲得的輸出以下:
1 2 |
存在session,browser=360 JSESSIONID : 320C21A645A160C4843D076204DA2F40 |
證明了一點,存放在客戶端的Cookie的確是存在安全問題的,咱們使用360的JSESSIONID「騙」過了服務器。畢竟,服務器只能經過Cookie中的JSESSIONID來辨別身份。(這提示咱們不要在公共場合保存Cookie信息,如今的瀏覽器在保存Cookie時一般會讓你肯定一次)
然而我使用以上Demo報出了Redis錯誤!通過排查和瀏覽官網找到了答案。
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration: Invocation of init method fail ed; nested exception is java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578)
完美解決!!!