理解 Web 中的Session


===================================
Session 工作原理是什么?
===================================
因為 http 協議是無狀態的, 對於服務器端來講, 如何為不同的訪問用戶提供不一樣的體驗呢? 比如郵箱系統, 只有登錄用戶才能收發郵件.
這就需要服務器能識別每一個客戶端訪問, 知道哪些訪問是來自一個同一個客戶端, 顯然這個事情光靠服務器端是做不到的, 需要瀏覽器配合才行, 瀏覽器端 cookie 概念就這么產生了.
Session 工作原理:
(1) 當一個 Session 第一次被啟動時, 服務器端先產生一個唯一的 session id, 並為該 session id 分配一個內存區, 用來保存該 session 相關的信息. 然后將該 id 存儲到瀏覽器的 cookie 中, cookie name 為 JSESSIONID.
(2) 瀏覽器每次訪問, 都會帶上本域下所有的 cookie, 包括這個 JSESSIONID cookie(應該是放到 Http Header 上的), 服務器端通過對比 JSESSIONID 和服務器內存中的 session id, 就能識別哪些訪問是來自同一個客戶端.
(3) 服務器端在每次訪問中, 都可以在 JSESSIONID 對應的 session 內存區中記錄一些信息, 比如最后一次的訪問時間, 比如訪問 IP 值, 以滿足業務需求.


===================================
Session 的用途有哪些?
===================================
1. 核心功能: 是識別每一個訪問.
2. 基本功能: 做登錄驗證, 然后就能得到登錄 id, 進而能完成權限驗證.
核心功能和基本功能都由 Spring Web 框架提供, 或 Shiro 這樣安全框架提供, 我們的代碼不用直接處理這些事情.
3. 利用 Session, 可以衍生出更多的功能, 比如保存訪客上次訪問的 IP 和上次訪問時間, 比如保存 user Id 和 user name, 這里稍微要多想一下:
(1). 這些數據既可以保存到 DB 中, 也可以保存到 Session 內存中, user id 和 user name 在 DB 中本來就保存着, 有必要在 session 中保存嗎?
Session 的優點是訪問速度快, 缺點是用戶量大的情況下內存占用是個問題.
(2). 這些數據既可以保存到 cookie , 也可以保存到 Session 內存中, 哪個更適合?
Cookie 的優點是: 不占用服務器端內存, Session 占服務器內存並且重啟后就沒了, cookie 可以被js訪問到, 常用的網頁埋點就是把一些標記信息保存到cookie, 然后由js提交到埋點服務器中.
Cookie 的缺點是, cookie 數據如果較大, 會消耗較多的網絡帶寬; 用戶隨意修改 cookie 值, 這樣的話, cookie的數據就不太可靠了; 另外, 用戶換個瀏覽器或者瀏覽器清個緩存, 原有的cookie就沒法用了.
這些問題都沒有標准答案, 按照具體情形做選擇吧.

 

===================================
JSESSIONID Cookie 是到底是在什么時候生成?
===================================
JSESSIONID Cookie 並不是在第一次訪問網站時候立即生成的, 只有訪問 url 對應視圖函數中有 Session 相應操作時, 瀏覽器端才會創建 JSESSIONID cookie 的.
當然, 一般網站在首頁訪問的視圖函數基本都會有 session 操作, 所以給我們的錯覺是, 一旦新訪問一個網站, JSESSIONID cookie 就自動產生了.

比如訪問 http://localhost:8080/ 主頁,  訪問第一個測試代碼的首頁, 並不會創建 JSESSIONID cookie. 

// 該 url 請求沒有任何與 session 相關的操作, 瀏覽器端不會創建該網站的 JSESSIONID cookie
@GetMapping("/")
public String index(HttpServletRequest request) throws SQLException {
    return "OK";
}

// 在 url 請求中, 有獲取 session 對象的指令, 瀏覽器端會創建該網站的 JSESSIONID cookie
@GetMapping("/")
public String index(HttpServletRequest request) throws SQLException {
    request.getSession(); //獲取 session 對象
    return "OK";
}    

// 在 url 請求中, 視圖函數要注入 session 對象, 瀏覽器端會創建該網站的 JSESSIONID cookie
@GetMapping("/")
public String index(HttpSession session) throws SQLException {
    return "OK";
}   

===================================
JSESSIONID 的 ID 值會登錄后會變化嗎?
===================================
假設在登錄之前, 瀏覽器端已經有了 JSESSIONID cookie. 現在問題來了, 在用戶登錄之后, 該 JSESSIONID ID 值是否變化?
答案是: 早期網站, 登錄前后 JSESSIONID 是不變的, 但后來發現, 如果登錄前后 JSESSIONID 不變, 會有中間人攻擊風險. 所以現在的安全框架在登錄完成后, 會自動修改 JSESSIONID 值. 我們開發網站不需要再專門修改 JSESSIONID.

和登錄不一樣, 登錄退出 JSESSIONID 的 Id 值並不會改變.


===================================
Session 和 cookie 之間的關系?
===================================
前面已經提到為了標識同一個客戶端訪問, 光靠服務器端的 Session 是不夠的, 還需要客戶端 JSESSIONID Cookie 支持. 這體現了 Session 和 cookie 之間最重要的一個關系.
另外, 在 Web 開發中 Session 和 Cookie 有一些很類似的方法, 往往會引起混淆, 有必要區別一下.
1. Session:

  •  保存在服務器端內存中, 服務器重啟所有的 session 都沒了.
  • session.setAttribute() 是在服務端內存中保存一個屬性, 並不會將它保存到瀏覽器端的 Cookie 中.
  • 可以在一個 session id 下新建多個屬性.
  • session 的有效期是針對所有 session id 全局級別的, 不是單個 session id 級, 也不是屬性級的.
  • Session 中 id 和對應的屬性, 可以大致理解為從屬關系, 這些屬性屬於一個 id.

2. Cookie:

  • 保存在瀏覽器本地存儲中, 服務器重啟並不會影響 cookie.
  • 一個網站下可以包含多個 cookie , 最重要的那個是 JSESSIONID Cookie. 新建一個 cookie 並不會在服務器端生成一個對應的屬性.
  • 每個 cookie 都可以設置自己的有效期, 自己的 HttpOnly 屬性.
  • 一個網站的 JSESSIONID Cookie 和其他 cookie 並沒有從屬關系

===================================
Spring 中 Session 和 cookie 的操作方式
===================================

Spring Session操作很簡單:

  • //獲取 session 對象, 或者視圖函數中直接注入 HttpSession 對象
  • request.getSession()
  • //設置一個 session 屬性
  • session.setAttribute()
  • //讀取一個 session 屬性
  • session.getAttribute()

 

1. 在視圖函數中使用 @CookieValue 修飾形參, 可注入一個 cookie 屬性值.

    @RequestMapping("/read")
    @ResponseBody
    public String read(@CookieValue(value = "foo", defaultValue = "hello") String fooCookie) {
        System.out.println(fooCookie);
        return fooCookie;
    }

2. 使用 HttpServletResponse.addCookie() 完成 cookie 的寫入. 

    @RequestMapping("/write")
    @ResponseBody
    public String write(HttpServletResponse response) {
        Cookie foo = new Cookie("foo", "bar"); //bake cookie
        foo.setMaxAge(1000); //set expire time to 1000 sec
        response.addCookie(foo); //put cookie in response
        return "write done";
    }

 

==================================
ajax 調用過程中是否會附加cookie信息?
==================================
如果 web 應用通過 jquery ajax 調用后台 api url, 調用過程是否會附加cookie信息, 要看情況:
1. 如果是同域, ajax 請求會自動帶上本域的cookie, 這台web服務器本身也有相應的 session 信息, 所以ajax請求自然能通過 session-cookie 完成身份驗證, 整個過程非常自然.
2. 如果 ajax調用的api 是另一個域下的url, 因為不是同域, 需要手動在ajax調用時加上cookie, 可參考:https://blog.csdn.net/wzl002/article/details/51441704, 服務器端返回response也需要做相應的處理, 另外也需要引入分布式session存儲. 

 

==================================
SSO/JWT/Session/OAuth2和web項目安全檢查的關系
==================================
1. OAuth2: 它是一個第三方認證機制, 適合To C的應用場景, 比如我們開發一個app, 可以借用微信/微博用戶認證開放接口, 達到免注冊登陸, 企業內部系統一般沒必要引入該技術.
2. SSO: 有兩個作用, 1: 保持登陸狀態, 2: 識別 username
3. JWT: 它是一個 token 生成技術, 可以包含 username 等信息.
4. Session: Session 一般用在web application中, 用來識別訪問者.

一般項目認證方式:
1. 對於 web application 項目, 一般使用 Session 來識別訪問者, 開發容易, 可以采用普通的 Login form 登陸, 也可以采用SSO登陸.
SSO 登陸詳細過程, 可參考張開濤博客<<shiro 單點登錄>> https://www.vxzsk.com/1197.html

2. 對於微服務項目, 一般和 OAuth2/Session 無關, 多數基於 jwt 的無狀態認證機制, 通過 jwt 來識別訪問者, 不過還是推薦將jwt前移到api 網關較好, 微服務僅僅關注業務邏輯.
完整微服務認證過程是: SSO登陸驗證通過后, SSO回調到統一auth服務來生成一個jwt token, 並傳給客戶端, 客戶端可將 jwt token保存到本地(web 客戶端可保存到 cookie 中), 每次客戶端請求需要將 jwt token 加到 http Authorization header上, 服務器端僅需要 jwt 算法就可以完成驗證(通常不會再查詢數據庫驗證), 並能提取 username 信息.
詳細過程和源碼, 可參考下面幾個文章:
    張開濤博客<<shiro 無狀態Web集成>>
        https://www.vxzsk.com/1233.html
    詳細shiro jwt 構建無狀態分布式鑒權體系
        https://www.jianshu.com/p/0a5d3d07a151
        https://www.jianshu.com/p/b0a577708a7b
    Shiro+JWT+Spring Boot Restful簡易教程
        https://www.jianshu.com/p/f37f8c295057

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM