0

Spring Security 3.1 と LDAP が統合されたアプリケーションがあります。以下は、これまでの要件と実装における重要なポイントです。

  1. アプリケーションは 1 人のユーザーに対して複数の役割を持ちますが、これらの役割は LDAP には存在しないため、アプリケーションは LDAP からのユーザー名 (またはユーザー ID) のみを認証します。
  2. ロールはデータベースに個別に保存されます
  3. LDAP からの認証が成功すると、UserDetailsS​​ervice を実装することにより、userdetails とロールがプリンシパル オブジェクトのカスタム userdetails オブジェクトに設定されます。

問題:

  1. ユーザー A がアプリケーションにログインする
  2. ユーザー B がアプリケーションにログインすると、ユーザー A のセッションが破棄されます (ユーザー A がまだログアウトしていないため、これは発生しないはずです!)
  3. ユーザー B がログアウトすると、ユーザー A は、ユーザー B がログインしたときにセッションが既に破棄されているため、ページが見つからないことを取得します。

applicationContext-security.xml は次のようになります。


<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <beans:property name="loginFormUrl" value="/login.jsp" />
    <beans:property name="forceHttps" value="true" /> 
</beans:bean>   

<beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:property name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="expiredUrl" value="/login.jsp?login_error=2" />
</beans:bean>

<beans:bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <beans:constructor-arg value="/login.jsp" />
    <beans:constructor-arg>
        <beans:list>
            <beans:ref bean="logoutEventBroadcaster" />
            <beans:bean id="securityContextLogoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
        </beans:list>
    </beans:constructor-arg>
    <beans:property name="filterProcessesUrl" value="/j_spring_security_logout" />
</beans:bean>

<beans:bean id="myAuthFilter" class="com.*.security.CustomAuthenticationProcessingFilter">  
    <beans:property name="sessionAuthenticationStrategy" ref="sas" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationFailureHandler" ref="failureHandler" />
    <beans:property name="authenticationSuccessHandler" ref="successHandler" />     
</beans:bean>

<authentication-manager alias="authenticationManager">
    <authentication-provider ref="adAuthenticationProvider" />
</authentication-manager>

<beans:bean id="adAuthenticationProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <beans:constructor-arg value="*.*.net" />
    <beans:constructor-arg value="ldap://*.*.net:389/" />
    <beans:property name="userDetailsContextMapper">
        <beans:bean class="com.ezadvice.service.CustomUserDetailsContextMapper" />
    </beans:property>       
    <beans:property name="useAuthenticationRequestCredentials" value="true" />
</beans:bean>

<beans:bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <beans:property name="defaultFailureUrl" value="/login.jsp?login_error=1" />
</beans:bean>

<beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <beans:property name="defaultTargetUrl" value="/home.do" />
    <beans:property name="alwaysUseDefaultTargetUrl" value="true"/>
</beans:bean>

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="maximumSessions" value="1" />
    <beans:property name="exceptionIfMaximumExceeded" value="true" />
    <beans:property name="migrateSessionAttributes" value="false" />
</beans:bean>

<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

CustomAuthenticationProcessingFilter クラスは次のようになります。

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException {

    String roleId = request.getParameter("roleId");
    String username = request.getParameter("j_username");
    TbEzaLoginHistory tbEzaLoginHistory = null;

    // check if the user has authority for the role
    TbEzaUser tbEzaUser = userManagementService.checkUserAndRole(roleId, username);
    if (null != tbEzaUser) {
        tbEzaLoginHistory = userManagementService.saveLoginHistory(tbEzaUser, roleId);
        request.setAttribute("loginHistoryId", tbEzaLoginHistory.getLoginKey());
        request.setAttribute("roleId", roleId);
        request.setAttribute("j_username", username);
        if (UserTracker.increment(username, roleId)) {
            try{
            Authentication attemptAuthentication = super.attemptAuthentication(request, response);
            if (null != attemptAuthentication) {
                CustomUser principal = (CustomUser) attemptAuthentication.getPrincipal();
                if (null == principal && null != tbEzaLoginHistory)
                        userManagementService.deleteFromLoginHistory(tbEzaLoginHistory.getLoginKey());  
                return attemptAuthentication;
            } 
            }
            catch (CommunicationException e) {
                userManagementService.deleteFromLoginHistory(tbEzaLoginHistory.getLoginKey());  
                UserTracker.decrement(username, roleId);        
                RequestDispatcher dispatcher = request.getRequestDispatcher("/login.jsp?login_error=5");                    
                try {
                    dispatcher.forward(request, response);
                } catch (ServletException se) {
                    se.printStackTrace();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
                LOGGER.debug("Connection Timeout error for UserName:"+username +"\n" + e);
                e.printStackTrace();                    
            }

        }else {
            if (null != tbEzaLoginHistory)
                userManagementService.deleteFromLoginHistory(tbEzaLoginHistory.getLoginKey());
            RequestDispatcher dispatcher = request.getRequestDispatcher("/login.jsp?login_error=4");
            try {
                dispatcher.forward(request, response);
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } else {
        RequestDispatcher dispatcher = request.getRequestDispatcher("/login.jsp?login_error=3");
        try {
            dispatcher.forward(request, response);
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug(EXITLOGGER + " attemptAuthentication");
    }

    return null;

}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
    super.successfulAuthentication(request, response, authResult);
    UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authResult;
    WebAuthenticationDetails details = (WebAuthenticationDetails) token.getDetails();
    String address = details.getRemoteAddress();
    CustomUser user = (CustomUser) authResult.getPrincipal();
    String userName = user.getUsername();
    System.out.println("Successful login from remote address: " + address + " by username: "+ userName);
}


@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException failed) throws IOException, ServletException {      
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug(ENTRYLOGGER + " unsuccessfulAuthentication");
    }
    try {           
        Long loginHistoryId = (Long) request.getAttribute("loginHistoryId");
        String username = (String) request.getAttribute("j_username");
        String roleId = (String) request.getAttribute("roleId");
        userManagementService.deleteFromLoginHistory(loginHistoryId);
        super.unsuccessfulAuthentication(request, response, failed);
        UserTracker.decrement(username, roleId);            
    } catch (Exception e) {
        e.printStackTrace();
    }       

    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug(EXITLOGGER + " unsuccessfulAuthentication");
    }
}

UserTracker クラスは次のようになります。

public class UserTracker {
private static Set<String> loggedInUsersDetails = new HashSet<String>();

@SuppressWarnings("unchecked")
synchronized public static boolean increment(String userName, String roleId) {  
    if(loggedInUsersDetails.add(userName.toLowerCase()+'~'+roleId)){
        return true;
    }else 
        return false;

    }       

synchronized public static void decrement(String userName, String roleId) {    
    loggedInUsersDetails.remove(userName.toLowerCase()+'~'+roleId);  
    } 

ユーザーAのセッションが破壊されている理由を誰かが教えてくれますか?

4

4 に答える 4

1

ドキュメント (SavedRequests および RequestCache インターフェイス)では、AuthenticationEntryPoint を呼び出す前に現在のリクエストをキャッシュする ExceptionTranslationFilter ジョブについて説明しています。これにより、SavedRequestAwareAuthenticationSuccessHandler (デフォルト) によって要求を復元できます。

しかし、私は別の evel フィルターに注目しました: RequestCacheAwareFilter.

元のリクエストへのリダイレクトの後、RequestCacheAwareFilter がチェーンによって呼び出され、リクエストを取得してキャッシュから削除する「getMatchingRequest()」を呼び出します。次に、2 番目の認証が (2 番目のユーザーから) 成功すると、キャッシュに URL がないため、Spring は私をリダイレクトする場所を認識しません。これが問題の根本原因だと思います。

この問題は、次の jira が原因で発生したことがわかりました: SEC-1241: SavedRequest は認証成功後に破棄されませんでした

于 2013-01-14T08:46:25.750 に答える
0

削除する

<beans:property name="maximumSessions" value="1" />

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="maximumSessions" value="1" />
    <beans:property name="exceptionIfMaximumExceeded" value="true" />
    <beans:property name="migrateSessionAttributes" value="false" />
</beans:bean>
于 2013-10-02T07:28:36.537 に答える
0

最後に、上記の問題の解決策を見つけました。複数の原因がありました:

  1. 上記の問題をテストしているときに、ユーザーがタブ付きブラウザーでアプリケーションを開いたときに同時実行制御を実現しようとしていたという間違いを犯していました。
  2. Spring は、複数のユーザーが同じマシンからログインするのを防ぐために、マシンの IP アドレスを内部的に保存します。したがって、複数の役割を持つユーザーが同じマシンからログインできないように、コードを変更する必要がありました。
于 2013-01-29T08:05:51.847 に答える
0

認証コードをカスタム AuthenticationManagerに移動できます。AuthenticationManager には、 LdapAuthenticationProviderDaoAuthenticationProviderに対する 2 つの依存関係があります。認証処理中に、次のことを担当します。

  • LDAP プロバイダーの呼び出し
  • DB プロバイダーの呼び出し
  • 2 つの認証オブジェクトを 1 つに結合します (LDAP からの資格情報と DB からのロール)。
于 2013-01-14T11:09:34.003 に答える