從零開始的Spring Session(一)

本文做者:徐靖峯
原文連接:http://t.cn/Rp7xUro
版權歸做者全部,轉載請註明出處html

 

以前項目須要使用Redis管理Session,找了不少的Demo,研究了一天Spring Session源碼,感受有點難繼續。忽然看到羣裏Pivotal的周輝大牛發了一篇鏈接,忽然豁然開朗。html5

 

Session和Cookie這兩個概念,在學習java web開發之初,大多數人就已經接觸過了。最近在研究跨域單點登陸的實現時,發現對於Session和Cookie的瞭解,並非很深刻,因此打算寫兩篇文章記錄一下本身的理解。在咱們的應用集成Spring Session以前,先補充一點Session和Cookie的關鍵知識。java

Session與Cookie基礎

因爲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瀏覽器訪問時,JSESSIONID爲4CD1D96E04FC390EA6C60E8C40A636AF,後端session記錄的值爲:browser=chrome
  • 360瀏覽器訪問時,JSESSIONID爲320C21A645A160C4843D076204DA2F40,後端session記錄的值爲:browser=360。

咱們使用chrome插件Edit this Cookie,將chrome瀏覽器中的JSESSIONID修改成360瀏覽器中的值

EditThisCookieEditThisCookie

一樣訪問原來的端點: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)

 

完美解決!!!

相關文章
相關標籤/搜索