Spring Security session 管理

2018-09-28 19:18 更新

session 管理

Spring Security 通過(guò) http 元素下的子元素 session-management 提供了對(duì) Http Session 管理的支持。

檢測(cè) session 超時(shí)

Spring Security 可以在用戶使用已經(jīng)超時(shí)的 sessionId 進(jìn)行請(qǐng)求時(shí)將用戶引導(dǎo)到指定的頁(yè)面。這個(gè)可以通過(guò)如下配置來(lái)實(shí)現(xiàn)。

   <security:http>
      ...
      <!-- session 管理,invalid-session-url 指定使用已經(jīng)超時(shí)的 sessionId 進(jìn)行請(qǐng)求需要重定向的頁(yè)面 -->
      <security:session-management invalid-session-url="/session_timeout.jsp"/>
      ...
   </security:http>

需要注意的是 session 超時(shí)的重定向頁(yè)面應(yīng)當(dāng)是不需要認(rèn)證的,否則再重定向到 session 超時(shí)頁(yè)面時(shí)會(huì)直接轉(zhuǎn)到用戶登錄頁(yè)面。此外如果你使用這種方式來(lái)檢測(cè) session 超時(shí),當(dāng)你退出了登錄,然后在沒(méi)有關(guān)閉瀏覽器的情況下又重新進(jìn)行了登錄,Spring Security 可能會(huì)錯(cuò)誤的報(bào)告 session 已經(jīng)超時(shí)。這是因?yàn)榧词鼓阋呀?jīng)退出登錄了,但當(dāng)你設(shè)置 session 無(wú)效時(shí),對(duì)應(yīng)保存 session 信息的 cookie 并沒(méi)有被清除,等下次請(qǐng)求時(shí)還是會(huì)使用之前的 sessionId 進(jìn)行請(qǐng)求。解決辦法是顯示的定義用戶在退出登錄時(shí)刪除對(duì)應(yīng)的保存 session 信息的 cookie。

   <security:http>
      ...
      <!-- 退出登錄時(shí)刪除 session 對(duì)應(yīng)的 cookie -->
      <security:logout delete-cookies="JSESSIONID"/>
      ...
   </security:http>

此外,Spring Security 并不保證這對(duì)所有的 Servlet 容器都有效,到底在你的容器上有沒(méi)有效,需要你自己進(jìn)行實(shí)驗(yàn)。

concurrency-control

通常情況下,在你的應(yīng)用中你可能只希望同一用戶在同時(shí)登錄多次時(shí)只能有一個(gè)是成功登入你的系統(tǒng)的,通常對(duì)應(yīng)的行為是后一次登錄將使前一次登錄失效,或者直接限制后一次登錄。Spring Security 的 session-management 為我們提供了這種限制。

首先需要我們?cè)?web.xml 中定義如下監(jiān)聽(tīng)器。

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

在 session-management 元素下有一個(gè) concurrency-control 元素是用來(lái)限制同一用戶在應(yīng)用中同時(shí)允許存在的已經(jīng)通過(guò)認(rèn)證的 session 數(shù)量。這個(gè)值默認(rèn)是 1,可以通過(guò) concurrency-control 元素的 max-sessions 屬性來(lái)指定。

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

當(dāng)同一用戶同時(shí)存在的已經(jīng)通過(guò)認(rèn)證的 session 數(shù)量超過(guò)了 max-sessions 所指定的值時(shí),Spring Security 的默認(rèn)策略是將先前的設(shè)為無(wú)效。如果要限制用戶再次登錄可以設(shè)置 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>

設(shè)置 error-if-maximum-exceeded 為 true 后如果你之前已經(jīng)登錄了,然后想再次登錄,那么系統(tǒng)將會(huì)拒絕你的登錄,同時(shí)將重定向到由 form-login 指定的 authentication-failure-url。如果你的再次登錄是通過(guò) Remember-Me 來(lái)完成的,那么將不會(huì)轉(zhuǎn)到 authentication-failure-url,而是返回未授權(quán)的錯(cuò)誤碼 401 給客戶端,如果你還是想重定向一個(gè)指定的頁(yè)面,那么你可以通過(guò) session-management 的 session-authentication-error-url 屬性來(lái)指定,同時(shí)需要指定該 url 為不受 Spring Security 管理,即通過(guò) http 元素設(shè)置其 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”,同時(shí)我們通過(guò) 指定了以 “/none” 開(kāi)始的所有 URL 都不受 Spring Security 控制,這樣當(dāng)用戶進(jìn)行登錄以后,再次通過(guò) Remember-Me 進(jìn)行自動(dòng)登錄時(shí)就會(huì)重定向到 “/none/session_authentication_error.jsp” 了。

在上述配置中為什么我們需要通過(guò) 指定我們的 session-authentication-error-url 不受 Spring Security 控制呢?把它換成 不行嗎?這就涉及到之前所介紹的它們兩者之間的區(qū)別了。前者表示不使用任何 Spring Security 過(guò)濾器,自然也就不需要通過(guò) Spring Security 的認(rèn)證了,而后者是會(huì)被 Spring Security 的 FilterChain 進(jìn)行過(guò)濾的,只是其對(duì)應(yīng)的 URL 可以匿名訪問(wèn),即不需要登錄就可訪問(wèn)。使用后者時(shí),REMEMBER_ME_FILTER 檢測(cè)到用戶沒(méi)有登錄,同時(shí)其又提供了 Remember-Me 的相關(guān)信息,這將使得 REMEMBER_ME_FILTER 進(jìn)行自動(dòng)登錄,那么在自動(dòng)登錄時(shí)由于我們限制了同一用戶同一時(shí)間只能登錄一次,后來(lái)者將被拒絕登錄,這個(gè)時(shí)候?qū)⒅囟ㄏ虻?session-authentication-error-url,重定向訪問(wèn) session-authentication-error-url 時(shí),經(jīng)過(guò) REMEMBER_ME_FILTER 時(shí)又會(huì)自動(dòng)登錄,這樣就形成了一個(gè)死循環(huán)。所以 session-authentication-error-url 應(yīng)當(dāng)使用 設(shè)置為不受 Spring Security 控制,而不是使用 。

此外,可以通過(guò) expired-url 屬性指定當(dāng)用戶嘗試使用一個(gè)由于其再次登錄導(dǎo)致 session 超時(shí)的 session 時(shí)所要跳轉(zhuǎn)的頁(yè)面。同時(shí)需要注意設(shè)置該 URL 為不需要進(jìn)行認(rèn)證。

   <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 固定攻擊保護(hù)

session 固定是指服務(wù)器在給客戶端創(chuàng)建 session 后,在該 session 過(guò)期之前,它們都將通過(guò)該 session 進(jìn)行通信。session 固定攻擊是指惡意攻擊者先通過(guò)訪問(wèn)應(yīng)用來(lái)創(chuàng)建一個(gè) session,然后再讓其他用戶使用相同的 session 進(jìn)行登錄(比如通過(guò)發(fā)送一個(gè)包含該 sessionId 參數(shù)的鏈接),待其他用戶成功登錄后,攻擊者利用原來(lái)的 sessionId 訪問(wèn)系統(tǒng)將和原用戶獲得同樣的權(quán)限。Spring Security 默認(rèn)是對(duì) session 固定攻擊采取了保護(hù)措施的,它會(huì)在用戶登錄的時(shí)候重新為其生成一個(gè)新的 session。如果你的應(yīng)用不需要這種保護(hù)或者該保護(hù)措施與你的某些需求相沖突,你可以通過(guò) session-management 的 session-fixation-protection 屬性來(lái)改變其保護(hù)策略。該屬性的可選值有如下三個(gè)。

  • migrateSession:這是默認(rèn)值。其表示在用戶登錄后將新建一個(gè) session,同時(shí)將原 session 中的 attribute 都 copy 到新的 session 中。
  • none:表示繼續(xù)使用原來(lái)的 session。
  • newSession:表示重新創(chuàng)建一個(gè)新的 session,但是不 copy 原 session 擁有的 attribute。
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)