1. 程式人生 > >spring security 概述& 配置檔案詳解

spring security 概述& 配置檔案詳解

通常,安全任務是由應用伺服器完成使用者認證和對資源的授權,這些任務也可以委託給Spring security處理這些任務從而減輕應用伺服器負擔,Spring安全基本上通過實施標準的javax.servlet.Filter來處理這些任務,您需要宣告下面的過濾器在web.xml

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

過濾器(springSecurityFilterChain)委託給Spring對在過濾器中定義的安全過濾器對上下文應用進行處理。在DelegatingFilterProxy類(實施javax.servlet.Filter的的)的doFilter方法??裡,Spring應用程式上下文將被名為'springSecurityFilterChain'進行檢查。

'springSecurityFilterChain'有一個別名filterChainProxy:

<alias name="filterChainProxy" alias="springSecurityFilterChain"/>

下面的問題是:

  • 誰初始化/定義此filterChainProxy?
  • 哪些安全過濾器在Spring應用程式上下文中定義?
  • 這些安全過濾器如何不同於在web.xml中定義的正常過濾器?

filterChainProxy是在應用程式上下文的安全名稱空間<HTTP>元素定義時初始化的。這裡是<HTTP>元件的基本結構:

<sec:http auto-config="true">
 <sec:intercept-url pattern="/**" access="ROLE_USER" />
</sec:http>

<sec:authentication-manager id="authenticationManager">
  <sec:authentication-provider>
    <sec:user-service>
      <sec:user name="admin" password="password" authorities="ROLE_USER, ROLE_ADMIN" />
      <sec:user name="user" password="password" authorities="ROLE_USER" />
    </sec:user-service>
  </sec:authentication-provider>
</sec:authentication-manager>

來自於Spring框架的HttpSecurityBeanDefinitionParser讀取這個‹http›元素,然後註冊 filterChainProxy到Spring的應用上下文. http元素的auto-config設定為真,實際就是處理如下簡寫:

<sec:http>
  <sec:form-login />
  <sec:http-basic />
  <sec:logout />
</sec:http>

下面回答第二個問題:哪些安全過濾器在Spring應用程式上下文中定義?

該<HTTP>名稱空間塊總是建立一個SecurityContextPersistenceFilter和一個ExceptionTranslationFilter和FilterSecurityInterceptor。這些都是固定的,不能被替換的替代品。

在預設情況下,當我們新增<HTTP>元素時,上面的三個過濾器將被新增。正如我們已經設定自動配置為true,BasicAuthenticationFilter,LogoutFilter和UsernamePasswordAuthenticationFilter也被新增到過濾器鏈。

現在,如果你看看任何這些過濾器的原始碼,這些也都是標準javax.servlet.Filter的實現。但是,通過定義這些過濾器在應用程式上下文中,而不是在web.xml中,應用伺服器將控制權交給Spring來處理安全相關的任務。Spring的filterChainProxy將負責請求的安全過濾。這回答了第三個問題。

為了獲得更精細的控制,可以定義自己的FilterChainProxy

<bean id="filterChainProxy" class="org.framework.security.web.FilterChainProxy">
  <sec:filter-chain-map path-type="ant">
    <sec:filter-chain pattern="/images/*" filters="none"/>
    <sec:filter-chain pattern="/**" filters="securityContextFilter, logoutFilter, formLoginFilter, servletApiFilter, anonFilter, exceptionTranslator, 
    filterSecurityInterceptor, customFilter1, customeFilter2" />
  </sec:filter-chain-map>
</bean>

從上面的XML中,我們看到我們不希望影象被應用於的任何過濾器檢查,請求的其餘部分都被指定有一個過濾器列表。但這種註冊自己的過濾器鏈一般是沒有必要的。Spring通過<HTTP>元素,提供了一些鉤子,通過它我們可以得到關於如何安全應用更精細的控制。因此,我們將著眼於通過<HTTP>元素進行配置細節。

下面我們看看Spring是如何完成以下功能:

  1. 驗證: HttpBasicAuthentication 和表單登入驗證
  2. 通過ACL實現授權控制
  3. 退出
  4. 匿名登入Anonymous Login support
  5. Remember-me 驗證
  6. 併發會話管理。

驗證

  HttpBasicAuthentication和基於表單的身份驗證登入 - 身份驗證可以通過兩種方式來處理。我們要定義適用所有的應用程式的身份驗證程式UserDetailsService:

package org.springframework.security.core.userdetails;

import org.framework.dao.DataAccessException;

/**
 * Core interface which loads user-specific data.
 * It is used throughout the framework as a user DAO and is the strategy used by the
 * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider DaoAuthenticationProvider}.
 * The interface requires only one read-only method, which simplifies support for new data-access strategies.
 * @see org.framework.security.authentication.dao.DaoAuthenticationProvider
 * @see UserDetails
 * @author Ben Alex
 */
public interface UserDetailsService {
    /**
     * Locates the user based on the username. In the actual implementation, the search may possibly be case
     * insensitive, or case insensitive depending on how the implementation instance is configured. In this case, the
     * <code>UserDetails</code> object that comes back may have a username that is of a different case than what was
     * actually requested..
     *
     * @param username the username identifying the user whose data is required.
     *
     * @return a fully populated user record (never <code>null</code>)
     *
     * @throws UsernameNotFoundException if the user could not be found or the user has no GrantedAuthority
     * @throws DataAccessException if user could not be found for a repository-specific reason
     */
    UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException, DataAccessException;
}

Spring提供這個介面的兩個實現:

(1)儲存使用者的 login/password 細節在應用上下文

<sec:authentication-manager id="authenticationManager">
  <sec:authentication-provider>
     <sec:user-service>
      <sec:user name="admin" password="password" authorities="ROLE_ADMIN,ROLE_USER"/>
      <sec:user name="user" password="password" authorities="ROLE_USER"/>
    </sec:user-service>
  </sec:authentication-provider>
</sec:authentication-manager>

‹authentication-provider›實際是配置DaoAuthenticationProvider類,這個Dao實際是UserDetailsService 的實現. 我們提供使用者名稱和密碼在XML中,當用戶很多時,應該儲存在資料庫中,相應實現是org.framework.security.core.userdetails.memory.InMemoryDaoImpl

(2)儲存使用者的 login/password 細節在資料庫

<sec:authentication-manager id="authenticationManager">
  <sec:authentication-provider>
    <sec:jdbc-user-service data-source-ref="dataSource" />
  </sec:authentication-provider>

</sec:authentication-manager>

這對應org.framework.security.core.userdetails.jdbc.JdbcDaoImpl. 類,如果你看看這個類,你可以發現,使用者名稱和密碼都儲存在使用者表,並且可以分配給使用者的角色儲存在授權表。下面是這個類從資料庫查詢獲取使用者憑據和許可權:

-- Fetch user credentials: 
select username,password,enabled from users where username = ?
-- Fetch user authorities: 
select username,authority from authorities where username = ?

假設你有您的使用者詳細資訊儲存在其他一些表的遺留資料庫,那麼我們可以配置Spring獲取使用者憑據和許可權。說我有一個成員表裡面有ID,使用者名稱,密碼和角色表有角色。下面是我們如何需要配置:

<sec:authentication-manager id="authenticationManager">
  <sec:authentication-provider>
    <!-- TBD <password-encoder hash="md5"/> -->
    <sec:jdbc-user-service id="userDetailsService" data-source-ref="dataSource" 
      users-by-username-query=
        "SELECT username, password, true as enabled
         FROM MEMBER
         WHERE username=?"
      authorities-by-username-query=
        "SELECT member.username, role.role as authorities
         FROM ROLE role, MEMBER member
         WHERE role.member_id=member.id and member.username=?"/>
  </sec:authentication-provider>
</sec:authentication-manager>

下面開始進行驗證。

HttpBasicAuthentication是如下配置

<sec:http auto-config="true">
<sec:http-basic />
</sec:http>

預設情況下,啟用此選項,瀏覽器通常會顯示使用者登入的登入對話方塊。而不是我們可以配置的登入頁面。它最大的問題,大多數瀏覽器都有傾向於快取的會話,不同的使用者無法在通過重新整理瀏覽器重新登入。

<http-basic>實際上定義了一個BasicAuthenticationFilter過濾器。一旦認證成功,認證物件將被投入Spring的SecurityContext。安全上下文可以通過類SecurityContextHolder進行訪問。下面是優化BasicAuthenticationFilter bean宣告:

<sec:custom-filter position="BASIC_AUTH_FILTER" ref="basicAuthenticationFilter" />

<bean id="basicAuthenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
</bean>

<bean id="authenticationEntryPoint" class="org.framework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <property name="loginFormUrl" value="/login.jsp"/>
</bean>

基於表單的驗證

<sec:form-login login-page="/login.jsp"/>啟用表單認證。

Spring提供了多個掛鉤。目標URL指定使用者進行身份驗證後應該去頁面,如果和身份驗證失敗,url定義認證失敗的頁面。

<sec:form-login login-page="/login.jsp" default-target-url="/app/messagePost" 
authentication-failure-url="/login.jsp?error=true"/>

更多屬性有: always-use-default-target, authentication-success-handler-ref and authentication-failure-handler-ref.authentication-success-handler-ref gets called on successful authentication and authentication-failure-handler-ref 

這裡是AuthenticationSuccessHandler 和AuthenticationFailureHandler.介面:

public interface AuthenticationSuccessHandler {

    void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException;
}

public interface AuthenticationFailureHandler {

    void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException;
}

Spring內建了兩個實現SimpleUrlAuthenticationSuccessHandler 和SavedRequestAwareAuthenticationSuccessHandler分別實現這兩個介面。

SavedRequestAwareAuthenticationSuccessHandler的目的是將使用者帶回定向到登入頁面的之前的那個頁面, ‹form-login› 預設是定義回到之前的那個頁面,我們可以優化定製登入成功後重定向到我們指定的那個頁面。

SimpleUrlAuthenticationFailureHandler和ExceptionMappingAuthenticationFailureHandler是進行失敗處理的,後者繼承前者。

只是在SimpleUrlAuthenticationFailureHandler 時規定一個出錯頁面的URL,ExceptionMappingAuthenticationFailureHandler( org.framework的子類。 security.core.AuthenticationException )我們指定的身份驗證型別的異常,使用者將被重定向到多個URL 。

當我們定義我們的自定義登入頁面,我們分別標記的使用者名稱和密碼欄位為j_username和j_password和提交操作將預設為j_spring_security_check 。我們也可以配置這些欄位名,並通過指定的屬性提交行動:分別為username-parameter, password-parameter 和 login-processing-url 。

完整的filter如下:

<sec:custom-filter position="FORM_LOGIN_FILTER" ref="formLoginFilter" />
<bean id="formLoginFilter" class="org.framework.security.web.authentication.UsernamePasswordAuthenticationFilter">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="filterProcessesUrl" value="/j_spring_security_check"/>
  <property name="usernameParameter" value="username "/>
  <property name="passwordParameter" value="password"/>
  <property name="authenticationSuccessHandler">
    <bean class="org.framework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler ">
      <property name="alwaysUseDefaultTargetUrl" value="true"/>
      <property name="defaultTargetUrl" value="/success.jsp"/>
    </bean>
  </property>
  <property name="authenticationFailureHandler">
    <!--bean class=" org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler "/-->
    <bean id="authenticationFailureHandler" class="org.framework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
      <property name="exceptionMappings">
        <props>
          <prop key="org.springframework.security.authentication.BadCredentialsException">/login/badCredentials</prop>
          <prop key="org.springframework.security.authentication.CredentialsExpiredException">/login/credentialsExpired</prop>
          <prop key="org.springframework.security.authentication.LockedException">/login/accountLocked</prop>
          <prop key="org.framework.security.authentication.DisabledException">/login/accountDisabled</prop>
        </props>
      </property>
    </bean>
  </property>
 </bean>

問題是使用者名稱和密碼是明文。這可以通過使用加密技術進行編碼的密碼處理。 Spring提供了一個內建該使用身份驗證提供程式使用<password-encoder>元素的支援。

<sec:authentication-manager id="authenticationManager">
  <sec:authentication-provider>
    <sec:password-encoder hash="md5"/>
    <sec:jdbc-user-service data-source-ref="dataSource" />
  </sec:authentication-provider>

</sec:authentication-manager>

通過ACL的授權

  通過‹http›的‹intercept-url›進行配置:

<sec:http access-decision-manager-ref="accessDecisionManager">
  <sec:intercept-url pattern="/app/messageList*" access="ROLE_USER,ROLE_ANONYMOUS"/>
  <sec:intercept-url pattern="/app/messagePost*" access="ROLE_USER"/>
  <sec:intercept-url pattern="/app/messageDelete*" access="ROLE_ADMIN"/>
  <sec:intercept-url pattern="/app/*" access="ROLE_USER"/>

  <form-login login-page="/login.jsp" default-target-url="/app/messagePost" 
    authentication-failure-url="/login.jsp?error=true"/>
  <!-- Other settings -->
</sec:http>

  每個 intercept-url 規定了一個 url模式和角色,具有這個角色的使用者可以訪問這些URL。注意url-pattern總是以 ‘*’結束,如果 ‘*’ 沒有規定,提供黑客在url中提供一些引數繞過安全機制。

Spring將這些URL攔截到FilterSecurityInterceptor. 下面是沒有使用‹intercept-url›:另外一種類似配置。

<sec:custom-filter position="FILTER_SECURITY_INTERCEPTOR" ref="filterSecurityInterceptor" />
<bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="accessDecisionManager" ref="DecisionManager"/>
  <property name="securityMetadataSource">
  <sec:filter-security-metadata-source lowercase-comparisons="true" request-matcher="ant" use-expressions="true">
    <sec:intercept-url pattern="/app/messageList*" access="ROLE_USER,ROLE_ANONYMOUS"/>
    <sec:intercept-url pattern="/app/messagePost*" access="ROLE_USER"/>
    <sec:intercept-url pattern="/app/messageDelete*" access="ROLE_ADMIN"/>
    <sec:intercept-url pattern="/app/*" ="ROLE_USER"/>
  </sec:filter-security-metadata-source>
  </property>
</bean>

從上面的程式碼可以看出,匿名使用者只能訪問messageList的頁面,其他頁面必須登入。

如果你仔細觀察的bean宣告,有一個屬性'的AccessDecisionManager “ 。這樣做的目的是什麼?

這個bean這實際上實現訪問控制決策。它有實現AccessDecisionManager介面。 Spring提供了三個內建的訪問決策管理。

  在瞭解訪問決策管理器是如何工作之前,我們需要知道AccessDecisionVoter是什麼。AccessDecisionManager實際上是由一個或多個決定是否訪問的投票者的組合體。這個組合封裝了允許/拒絕/放棄觀看資源的使用者邏輯。投票者決定結果是通過ACCESS_GRANTED , ACCESS_DENIED和ACCESS_ABSTAIN中的AccessDecisionVoter介面中定義的常量欄位來表示。我們可以定義自定義訪問決策,並注入到我們的訪問決策管理器中。

看看內建決策管理者:

AffirmativeBased :至少一個投票者必須決定授予訪問許可權
ConsensusBased :多數投票者必須授予訪問許可權
UnanimousBased :所有投票者都必須投票或放棄投票授予訪問許可權(無投票表決拒絕訪問)

預設情況下, AffirmativeBased訪問決策管理器將由兩個投票者初始化:RoleVoter和AuthenticatedVoter 。

如果使用者具有訪問資源的角色,RoleVoter授權訪問,角色必須有“ ROLE_ ”字首,下面我們看到也可以定製其他字首。

AuthenticatedVoter是在使用者被驗證是授權訪問,接受的身份驗證級別有:IS_AUTHENTICATED_FULLY , IS_AUTHENTICATED_REMEMBERED和IS_AUTHENTICATED_ANONYMOUSLY

下面展示假設我們要定義一個自定義的投票者,並把它新增到訪問決策管理器:

<sec:http -decision-manager-ref="accessDecisionManager" auto-config="true">
  <!-- filters declaration go here-->
</sec:http>

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
  <property name="decisionVoters">
    <list>
      <bean class="org.springframework.security.access.vote.RoleVoter">
       <!-- Customize the prefix-->
       <property name="rolePrefix" value="ROLE_"/>
      </bean>
      <bean class="org.springframework.security..vote.AuthenticatedVoter"/>
      <bean class="com.pramati.security.voters.CustomVoter"/>
    </list>
  </property>
</bean>

登入退出

  登入退出配置如下:

<sec:http>

  <!-- Other filter declarations here -->

  <sec:logout />

</sec:http>

預設登出頁面的URL是/j_spring_security_logout,可以規定 logout-url attribute.優化這個URL.

退出成功後,預設是根路徑,通過下面可以指定退出成功後的URL:

<sec:logout logout-url="/j_logMeOut" logout-success-url="/app/messageList"/>

如果你想在登陸頁面不是預設的,而是一個特定的在不同的場景不同URL,那麼我們必須實現LogoutSuccessHandler並提供了一個參考<logout>元素

<sec:logout logout-url="/j_logMeOut" success-handler-ref="customLogoutSuccessHandler"/>

然後定義Bean:

<sec:custom-filter position="LOGOUT_FILTER" ref="logoutFilter" />
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
  <constructor-arg value="/pages/Security/logout.html" />
    <constructor-arg>
      <list>
        <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
      </list>
    </constructor-arg>
  <property name="filterProcessesUrl" value="/j_logMeOut"/>
</bean>

匿名登入

  預設Spring建立anonymous角色。當你規定角色是ROLE_ANONYMOUS’ 或‘IS_AUTHENTICATED_ANONYMOUSLY’, 任何人都可以訪問資源。

在AffirmativedBased 訪問控制器中,RoleVoter看到‘ROLE_ANONYMOUS’設定就授權訪問,類似AuthenticatedVoter 看到‘IS_AUTHENTICATED_ANONYMOUSLY’.授權訪問。

假設你要分配給匿名使用者不同的角色名,則可以覆蓋預設的配置如下:

<sec:http>
  <sec:intercept-url pattern="/login.jsp*" filters="none"/>
  <sec:intercept-url pattern="/*" ="ROLE_USER"/>

  <!-- Defines a custom role in place of ROLE_ANONYMOUS. 
  ROLE_ANONYMOUS will no more work, use ROLE_GUEST instead of it-->
  <sec:anonymous username="guest" granted-authority="ROLE_GUEST" />
</sec:http>

<p style="text-align: justify;">Here is the how the underlying filter can be defined if you don't want to use ?anonymous? element:</p>

1
<sec:custom-filter position="ANONYMOUS_FILTER" ref="anonymousFilter" />
<bean id="anonymousFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter" >
    <property name="userAttribute" value="ROLE_GUEST" />
</bean>

記得我Remember-me驗證

  這是指網站能夠記住會話之間主要的標識。Spring通過傳送一個cookie到成功進行身份驗證的瀏覽器,在Cookie中是如下:

  base64(username + “:” + expirationTime + “:” + md5Hex(username + “:” + expirationTime + “:” password + “:” + key));

  當瀏覽器發出下一個請求到伺服器,它也隨之傳送這個cookie。Spring執行以下操作:
(一)從後端為給定的使用者名稱獲取密碼
(二)根據uername獲取從資料庫中pasword,並計算使用者名稱 密碼,expirationTime和key的md5Hex,並比較它在cookie的值
(三)如果它們匹配 - 成功登入!如果不匹配,那麼你已經提供的偽造Cookie或使用者名稱/密碼/金鑰中的一個發生了變化。

啟用 remember-me 通過http下面的配置:

<sec:http>

<!-- Other filter declarations here -->

<sec:remember-me key="myAppKey"/>

</sec:http>

有一點要注意的是,有一個潛在的安全問題在這裡,令牌可以被捕獲並可能被濫用,因為它是有效的,直到它過期。使用滾動令牌可避免。這裡是如何實現基於令牌的記得我的服務:

<sec:http access-decision-manager-ref="accessDecisionManager">

   <!-- Other filter declarations here -->

   <remember-me services-alias="rememberMeService" data-source-ref="dataSource"/>
   <!-- <remember-me data-source-ref="dataSource" key="pramati"/> -->

</sec:http>

<bean id="tokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
   <property name="dataSource" ref="dataSource"/>
   <property name="createTableOnStartup" value="true"/>
</bean>

<bean id="rememberMeService" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
   <property name="userDetailsService" ref="userDetailsService"/>
   <property name="tokenRepository" ref="tokenRepository"/>
</bean>

( a)在資料庫中必須建立新表persistent_logins,因為我們已經指定“ createTableOnStartup '在構造” tokenRepository “時 。下面是SQL建立表:

create table persistent_logins (
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null);

( b)我們沒有更多的定義我們自己的安全令牌。 Spring會自動生成令牌,並把persistent_tokens表更新。當用戶從一個瀏覽器通過選擇“記住我”的選項登入時,建立本表的一條記錄,下一次使用者從同一個瀏覽器登入時,使用者會被自動記錄,並將在資料庫中的令牌值更改為一個新的值,但該系列值保持不變。假設現在使用者從不同的瀏覽器轉而選擇記住我登入時,一個新的記錄將為該瀏覽器建立。當他訪問從瀏覽器訪問應用程式時,後續更新會發生。

  因此,使用這種方法的好處是,攻擊者將只能使用一個偷來的cookie,一直到受害者使用者下一次訪問應用程式之前,而不是非動態令牌時需要記住cookie的完整生命週期。當受害者接下來訪問網站,他將使用相同的cookie 。這時 Spring會丟擲一個CookieTheftException ,它可以用來通知盜竊發生的使用者。

下面是為安全鏈的自定義過濾器

<sec:custom-filter position="REMEMBER_ME_FILTER" ref="rememberMeFilter" />

<bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
  <property name="rememberMeServices" ref="rememberMeServices"/>
  <property name="authenticationManager" ref="theAuthenticationManager" />
</bean>

<bean id="tokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
   <property name="dataSource" ref="dataSource"/>
   <property name="createTableOnStartup" value="false"/>
</bean>

<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
   <property name="userDetailsService" ref="userDetailsService"/>
   <property name="tokenRepository" ref="tokenRepository"/>
</bean>

<bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.rememberme.RememberMeAuthenticationProvider"/>

併發會發管理

假設我們不希望使用者在應用的同時多個地方登入,下面是我們如何做到這一點:

<sec:http>

  <!-- Other filter declarations here -->

  <sec:session-management session-authentication-error-url="/login.jsp?error=alreadyLoggedin" >
    <sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true"
              expired-url="/login.jsp?error=alreadyLoggedin"/>
  </sec:session-management>
</sec:http>

或者我們在web.xml中定義監聽者,當用戶登出以後發出一個事件org.springframework.security.core.session.SessionDestroyedEvent:

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

Spring使用類似於我們一直在討論的安全過濾器。除此之外,還採用ApplicationEvents。當Spring看到所需要的併發控制,它可以維持與主要關聯會話列表。Map結構看起來像(在org.springframework.security.core.session.SessionRegistryImpl實際定義):

ConcurrentMap<Object,Set<String>> principals =
new ConcurrentHashMap<Object,Set<String>>();

這裡對映的鍵是使用者物件,值設定為與它相關聯的會話ID。所以,當這個資料集的大小超過在<concurrency-control>元素中定義MAX-會話的值將引發異常。當Spring看到定義的併發控制元件,SessionRegistryImpl(Map的定義地方)將其內部ConcurrentSessionControlStrategy注入UsernamePasswordAuthenticationFilter。當用戶認證成功,Spring放入Map一條記錄。

當用戶登出時,在web.xml中定義的偵聽器將發出SessionDestroyedEvent,SessionRegistryImpl偵聽此事件,並從維持map中刪除會話ID條目。沒有刪除掉,使用者將永遠無法重新登入,即使他們登出另一個會話或會話超時。因此,這裡是<concurrency-control>等效的配置:

<sec:http>
  <sec:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
  <sec:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />

  <!-- Other filter declarations here -->

相關推薦

spring security 概述& 配置檔案

通常,安全任務是由應用伺服器完成使用者認證和對資源的授權,這些任務也可以委託給Spring security處理這些任務從而減輕應用伺服器負擔,Spring安全基本上通過實施標準的javax.servlet.Filter來處理這些任務,您需要宣告下面的過濾器在web.xm

Spring Boot基礎教程 ( 四 ) :Spring Boot 屬性配置檔案

相信很多人選擇Spring Boot主要是考慮到它既能兼顧Spring的強大功能,還能實現快速開發的便捷。我們在Spring Boot使用過程中,最直觀的感受就是沒有了原來自己整合Spring應用時繁多的XML配置內容,替代它的是在pom.xml中引入模組化的Starter

Spring Boot 核心配置檔案

用過 Spring Boot 的都知道在 Spring Boot 中有以下兩種配置檔案 bootstrap (.yml 或者 .properties) application (.yml 或者 .properties) 為什麼會有這兩種配置檔案呢?大家

Spring Boot屬性配置檔案

相信很多人選擇Spring Boot主要是考慮到它既能兼顧Spring的強大功能,還能實現快速開發的便捷。我們在Spring Boot使用過程中,最直觀的感受就是沒有了原來自己整合Spring應用時繁多的XML配置內容,替代它的是在pom.xml中引入模組化的Starte

Spring Boot 屬性配置檔案

SpringBoot 配置檔案預設為application.properties,但現在的趨勢是使用yaml,它是類似於標準通用標記語言的子集XML的資料描述語言,語法比XML簡單很多。applicat

轉:ssm spring+springmvc+mybatis中的xml配置檔案

這幾天一直在整合SSM框架,雖然網上有很多已經整合好的,但是對於裡面的配置檔案並沒有進行過多的說明,很多人知其然不知其所以然,經過幾天的搜尋和整理,今天總算對其中的XML配置檔案有了一定的瞭解,所以拿出來一起分享一下,希望有不足的地方大家批評指正~~~ 首先   這篇文章暫時只對框架中所要用到的配

Spring Boot(二)配置檔案

簡介 Spring Boot使用“習慣由於配置”(專案中存在大量預設的配置,而且內建tomcat)的理念,讓你的無需手動進行配置就可以將專案執行起來。使用Spring Boot很容易建立一個獨立執行的、準生產級別的Spring框架的專案。同時,Spring Boot還提供一個全域性配置檔案對一些

spring配置檔案--真的蠻詳細

  spring配置檔案詳解--真的蠻詳細 轉自: http://book.51cto.com/art/201004/193743.htm 此處詳細的為我們講解了spring2.5的實現原理,感覺非常有用

SpringBoot非官方教程 | 第二篇:Spring Boot配置檔案

springboot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨在讓您儘快啟動和執行。在一般情況下,我們不需要做太多的配置就能夠讓spring boot正常執行。在一些特殊的情況下,我們需要做修改一些配置,或者需要有自己的配置屬性。 當我們

Spring boot 配置檔案 (properties 和yml )

從其他框架來看 我們都有自己的配置檔案, hibernate有hbm,mybatis 有properties, 同樣, Spring boot 也有全域性配置檔案。 Springboot使用一個全域性的配置檔案,而且配置檔案的名字是固定的。 有兩種 application.properties

Spring Boot 配置檔案:自定義屬性、隨機數、多環境配置

相信很多人選擇Spring Boot主要是考慮到它既能兼顧Spring的強大功能,還能實現快速開發的便捷。我們在Spring Boot使用過程中,最直觀的感受就是沒有了原來自己整合Spring應用時繁多的XML配置內容,替代它的是在pom.xml中引入模組化的Starter POMs,其中各個模組都有自己的預

SpringBoot學習第二篇:Spring Boot配置檔案

原文首發於:https://www.fangzhipeng.com/springboot/2017/07/11/springboot2-config-file/ 本文出自方誌朋的部落格 springboot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨

Spring Boot配置檔案-ConfigurationProperties和Value優缺點-(轉)好文

文章轉自 http://www.cnblogs.com/itdragon/p/8686554.html Spring Boot提供了兩種常用的配置檔案,分別是properties檔案和yml檔案。他們的作用都是修改Spring Boot自動配置的預設值。相對於properties檔案而言,yml檔

RabbitMQ整合Spring配置檔案

一、rabbitmq 配置檔案 在web 專案開發過程中,一般分為生產者配置檔案和消費者配置檔案。 一、準備工作 安裝好rabbitmq,並在專案中增加配置檔案 rabbit.properties 內容如下: rmq.ip=192.188.113.114 rmq.port=

企業級 SpringBoot 教程 (二)Spring Boot配置檔案

springboot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨在讓您儘快啟動和執行。在一般情況下,我們不需要做太多的配置就能夠讓spring boot正常執行。在一些特殊的情況下,我們需要做修改一些配置,或者需要有自己的配置屬性

第二篇:Spring Boot配置檔案

Spring Boot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨在讓您儘快啟動和執行。在一般情況下,我們不需要做太多的配置就能夠讓Spring Boot正常執行。在一些特殊的情況下,我們需要做一些配置修改,或者配置自定義屬性。 自定義屬

轉載:SpringBoot非官方教程 | 第二篇:Spring Boot配置檔案

springboot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨在讓您儘快啟動和執行。在一般情況下,我們不需要做太多的配置就能夠讓spring boot正常執行。在一些特殊的情況下,我們需要做修改一些配置,或者需要

Spring的學習(八)——配置檔案

一、Bean標籤和屬性 bean標籤,是根標籤beans內部必須包含的標籤,它是用於宣告具體的類的物件! 二、bean標籤使用 ①name屬性:name不能重複,name可以指定多個,逗號隔開。可以使用特殊字元,也可以根據name屬性獲取一個物件 ②id屬性:

Spring Boot 配置檔案

簡介 Spring Boot使用“習慣由於配置”(專案中存在大量預設的配置,而且內建tomcat)的理念,讓你的無需手動進行配置就可以將專案執行起來。使用Spring Boot很容易建立一個獨立執行的、準生產級別的Spring框架的專案。同時,Spring Boot還提供一

SpringMVC配置檔案:<context:annotation-config/>和<context:component-scan base-package=""/>和<mvc:annotation-driven /> Spring配置

原文地址:https://www.cnblogs.com/lcngu/p/5080702.html Spring配置檔案詳解:<context:annotation-config/>和<context:component-scan base-package=""/>和<mvc: