0

我有一个带有 spring security 3.1 和 Ldap 集成的应用程序。以下是到目前为止的需求和实现中的关键点:

  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 接口)中,他们讨论了 ExceptionTranslationFilter 作业以在调用 AuthenticationEntryPoint 之前缓存当前请求。这允许通过 SavedRequestAwareAuthenticationSuccessHandler (这是默认设置)恢复请求。

但我注意到另一个级别过滤器:RequestCacheAwareFilter。

在重定向到原始请求之后,链调用 RequestCacheAwareFilter,他调用“getMatchingRequest()”,获取请求,然后将其从缓存中删除!然后,当第二次身份验证成功时(来自第二个用户),缓存中没有 URL,所以 Spring 不知道将我重定向到哪里。所以我相信这是问题的根本原因。

我发现这个问题是由于这个jira而产生的: SEC-1241: SavedRequest not destroy after successful authentication

于 2013-01-14T08:46:25.750 回答
0

您可以将您的身份验证代码移动到自定义 AuthenticationManager中。AuthenticationManager 将对LdapAuthenticationProviderDaoAuthenticationProvider有两个依赖项。在身份验证处理期间,它将负责:

  • 调用 LDAP 提供程序
  • 调用数据库提供者
  • 将两个身份验证对象合二为一(来自 LDAP 的凭据和来自 DB 的角色)。
于 2013-01-14T11:09:34.003 回答
0

终于找到了解决上述问题的办法。有多种原因:

  1. 在测试上述问题时,我犯了一个错误,即当用户在选项卡式浏览器中打开应用程序时,我试图实现并发控制。
  2. Spring内部存储了机器的ip地址,以防止多个用户从同一台机器登录。因此必须进行代码更改,以便不允许具有多个角色的用户从同一台机器登录。
于 2013-01-29T08:05:51.847 回答
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 回答