參考連結:https://xueliang.org/article/detail/20170302232815082

session 管理

Spring Security 通過 http 元素下的子元素 session-management 提供了對 Http Session 管理的支援。

檢測 session 超時

Spring Security 可以在使用者使用已經超時的 sessionId 進行請求時將使用者引導到指定的頁面。這個可以通過如下配置來實現。

   <security:http>
...
<!-- session 管理,invalid-session-url 指定使用已經超時的 sessionId 進行請求需要重定向的頁面 -->
<security:session-management invalid-session-url="/session_timeout.jsp"/>
...
</security:http>

需要注意的是 session 超時的重定向頁面應當是不需要認證的,否則再重定向到 session 超時頁面時會直接轉到使用者登入頁面。此外如果你使用這種方式來檢測 session 超時,當你退出了登入,然後在沒有關閉瀏覽器的情況下又重新進行了登入,Spring Security 可能會錯誤的報告 session 已經超時。這是因為即使你已經退出登入了,但當你設定 session 無效時,對應儲存 session 資訊的 cookie 並沒有被清除,等下次請求時還是會使用之前的 sessionId 進行請求。解決辦法是顯示的定義使用者在退出登入時刪除對應的儲存 session 資訊的 cookie。

   <security:http>
...
<!-- 退出登入時刪除 session 對應的 cookie -->
<security:logout delete-cookies="JSESSIONID"/>
...
</security:http>

此外,Spring Security 並不保證這對所有的 Servlet 容器都有效,到底在你的容器上有沒有效,需要你自己進行實驗。

concurrency-control

通常情況下,在你的應用中你可能只希望同一使用者在同時登入多次時只能有一個是成功登入你的系統的,通常對應的行為是後一次登入將使前一次登入失效,或者直接限制後一次登入。Spring Security 的 session-management 為我們提供了這種限制。

首先需要我們在 web.xml 中定義如下監聽器。

   <listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

在 session-management 元素下有一個 concurrency-control 元素是用來限制同一使用者在應用中同時允許存在的已經通過認證的 session 數量。這個值預設是 1,可以通過 concurrency-control 元素的 max-sessions 屬性來指定。

   <security:http auto-config="true">
...
<security:session-management>
<security:concurrency-control max-sessions="1"/>
</security:session-management>
...
</security:http>

當同一使用者同時存在的已經通過認證的 session 數量超過了 max-sessions 所指定的值時,Spring Security 的預設策略是將先前的設為無效。如果要限制使用者再次登入可以設定 concurrency-control 的 error-if-maximum-exceeded 的值為 true。

   <security:http auto-config="true">
...
<security:session-management>
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</security:session-management>
...
</security:http>

設定 error-if-maximum-exceeded 為 true 後如果你之前已經登入了,然後想再次登入,那麼系統將會拒絕你的登入,同時將重定向到由 form-login 指定的 authentication-failure-url。如果你的再次登入是通過 Remember-Me 來完成的,那麼將不會轉到 authentication-failure-url,而是返回未授權的錯誤碼 401 給客戶端,如果你還是想重定向一個指定的頁面,那麼你可以通過 session-management 的 session-authentication-error-url 屬性來指定,同時需要指定該 url 為不受 Spring Security 管理,即通過 http 元素設定其 secure=”none”。

   <security:http security="none" pattern="/none/**" />
<security:http>
<security:form-login/>
<security:logout/>
<security:intercept-url pattern="/**" access="ROLE_USER"/>
<!-- session-authentication-error-url 必須是不受 Spring Security 管理的 -->
<security:session-management session-authentication-error-url="/none/session_authentication_error.jsp">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</security:session-management>
<security:remember-me data-source-ref="dataSource"/>
</security:http>

在上述配置中我們配置了 session-authentication-error-url 為 “/none/session_authentication_error.jsp”,同時我們通過 指定了以 “/none” 開始的所有 URL 都不受 Spring Security 控制,這樣當用戶進行登入以後,再次通過 Remember-Me 進行自動登入時就會重定向到 “/none/session_authentication_error.jsp” 了。

在上述配置中為什麼我們需要通過 指定我們的 session-authentication-error-url 不受 Spring Security 控制呢?把它換成 不行嗎?這就涉及到之前所介紹的它們兩者之間的區別了。前者表示不使用任何 Spring Security 過濾器,自然也就不需要通過 Spring Security 的認證了,而後者是會被 Spring Security 的 FilterChain 進行過濾的,只是其對應的 URL 可以匿名訪問,即不需要登入就可訪問。使用後者時,REMEMBER_ME_FILTER 檢測到使用者沒有登入,同時其又提供了 Remember-Me 的相關資訊,這將使得 REMEMBER_ME_FILTER 進行自動登入,那麼在自動登入時由於我們限制了同一使用者同一時間只能登入一次,後來者將被拒絕登入,這個時候將重定向到 session-authentication-error-url,重定向訪問 session-authentication-error-url 時,經過 REMEMBER_ME_FILTER 時又會自動登入,這樣就形成了一個死迴圈。所以 session-authentication-error-url 應當使用 設定為不受 Spring Security 控制,而不是使用 。

此外,可以通過 expired-url 屬性指定當使用者嘗試使用一個由於其再次登入導致 session 超時的 session 時所要跳轉的頁面。同時需要注意設定該 URL 為不需要進行認證。

   <security:http auto-config="true">
<security:form-login/>
<security:logout/>
<security:intercept-url pattern="/expired.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**" access="ROLE_USER"/>
<security:session-management>
<security:concurrency-control max-sessions="1" expired-url="/expired.jsp" />
</security:session-management>
</security:http>

session 固定攻擊保護

session 固定是指伺服器在給客戶端建立 session 後,在該 session 過期之前,它們都將通過該 session 進行通訊。session 固定攻擊是指惡意攻擊者先通過訪問應用來建立一個 session,然後再讓其他使用者使用相同的 session 進行登入(比如通過傳送一個包含該 sessionId 引數的連結),待其他使用者成功登入後,攻擊者利用原來的 sessionId 訪問系統將和原使用者獲得同樣的許可權。Spring Security 預設是對 session 固定攻擊採取了保護措施的,它會在使用者登入的時候重新為其生成一個新的 session。如果你的應用不需要這種保護或者該保護措施與你的某些需求相沖突,你可以通過 session-management 的 session-fixation-protection 屬性來改變其保護策略。該屬性的可選值有如下三個。

  • migrateSession:這是預設值。其表示在使用者登入後將新建一個 session,同時將原 session 中的 attribute 都 copy 到新的 session 中。
  • none:表示繼續使用原來的 session。
  • newSession:表示重新建立一個新的 session,但是不 copy 原 session 擁有的 attribute。