天天看點

從零開始的Spring Session(一)

Session和Cookie這兩個概念,在學習java web開發之初,大多數人就已經接觸過了。最近在研究跨域單點登入的實作時,發現對于Session和Cookie的了解,并不是很深入,是以打算寫兩篇文章記錄一下自己的了解。在我們的應用內建Spring Session之前,先補充一點Session和Cookie的關鍵知識。

https://blog.didispace.com/spring-session-xjf-1/#Session%E4%B8%8ECookie%E5%9F%BA%E7%A1%80 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,他是真正維系用戶端和服務端的橋梁。

https://blog.didispace.com/spring-session-xjf-1/#%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B 代碼示例

使用者發起請求,伺服器響應請求,并做一些使用者資訊的處理,随後傳回響應給使用者;使用者再次發起請求,攜帶sessionId,伺服器便能夠識别,這個使用者就是之前請求的那個。

使用Springboot編寫一個非常簡單的服務端,來加深對其的了解。需求很簡單,當浏覽器通路

localhost:8080/test/cookie?browser=xxx

時,如果沒有擷取到session,則将request中的browser存入session;如果擷取到session,便将session中的browser值輸出。順便将request中的所有cookie列印出來。

@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機制是什麼。

1 使用chrome浏覽器,通路

localhost:8080/test/cookie?browser=chrome

,控制台輸出如下:

Session Info:   不存在session,設定browser=chrome      

既沒有session,也沒有cookie,并且根據我們将browser=chrome已經設定到了session中。

再次通路同樣的位址,控制台輸出如下:

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

第一次通路:

Session Info:   不存在session,設定browser=360      

後續通路:

Session Info:   存在session,browser=360
Cookie Info:    JSESSIONID : 320C21A645A160C4843D076204DA2F40      

為什麼要再次使用另一個浏覽器通路呢?先賣個關子,我們最起碼可以得出結論,不同浏覽器,通路是隔離的,甚至重新打開同一個浏覽器,JSESSIONID也是不同的。

https://blog.didispace.com/spring-session-xjf-1/#%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98 安全問題

其實上述的知識點,都是非常淺顯的,之是以啰嗦一句,是為了引出這一節的内容,以及友善觀察後續我們引入Spring Session之後的發生的變化。

還記得上一節的代碼示例中,我們使用了兩個浏覽器:

  • chrome浏覽器通路時,JSESSIONID為4CD1D96E04FC390EA6C60E8C40A636AF,後端session記錄的值為:browser=chrome
  • 360浏覽器通路時,JSESSIONID為320C21A645A160C4843D076204DA2F40,後端session記錄的值為:browser=360。

我們使用chrome插件Edit this Cookie,将chrome浏覽器中的JSESSIONID修改為360浏覽器中的值

從零開始的Spring Session(一)

同樣通路原來的端點:

localhost:8080/test/cookie?browser=chrome

,得到的輸出如下:

存在session,browser=360
JSESSIONID : 320C21A645A160C4843D076204DA2F40      

證明了一點,存放在用戶端的Cookie的确是存在安全問題的,我們使用360的JSESSIONID“騙”過了伺服器。畢竟,伺服器隻能通過Cookie中的JSESSIONID來辨識身份。(這提示我們不要在公共場合儲存Cookie資訊,現在的浏覽器在儲存Cookie時通常會讓你确定一次)

下一篇文章,将正式講解如何在應用中內建Spring Session。