Announcement Announcement Module
Collapse
No announcement yet.
Is J2eePreAuthenticatedProcessingFilter incompatible? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Is J2eePreAuthenticatedProcessingFilter incompatible?

    I'm working on a Flex / Spring application that uses Flex's HttpService to post credentials to Spring Security's form authentication. I also have the J2eePreAuthenticationProcessingFilter in place and working. None of this has been using Spring-BlazeDS Integration.

    Now that Spring-BlazeDS is coming along so nicely, I'm trying to integrate it into my project, and have switched from the Flex HttpService to the ChannelSet login on the form. However, there seems to be an incompatibility. Here is the use case:

    1. Hit the app for the first time
    2. The user is automatically logged in via pre-authentication
    3. Sign out (the HttpSession and FlexSession are invalidated)
    4. Attempt to login as a different user

    This results in "Cannot re-authenticate in the same session." The gist of the problem is that the call to ChannelSet login is pre-authenticating again before reaching the SpringSecurityLoginCommand. Is there a way around this problem, or is pre-authentication + form-login incompatible with BlazeDS ChannelSet login?

    Thanks for any advice!

    Adam

  • #2
    Some quick questions to figure out if it's something we already fixed:

    1. Are you using RC1? If not, that's the first thing to do.
    2. Have you tried configuring Spring Security with its session fixation protection turned off? (There is a bug in RC1 that causes it not to work in all possible configurations that we have since fixed.) If not, try something along these lines in your security config:
    Code:
    <http session-fixation-protection="none" >
      ...
    </http>

    Comment


    • #3
      yes to both questions

      Hi Jeremy,

      Yes, I am using RC1, and yes I do have session-fixation-protection="none" (in fact the Spring context initialization fails if session fixation is not turned off - org.springframework.beans.factory.NoSuchBeanDefini tionException: No bean named '_filterChainList' is defined).

      Adam

      Comment


      • #4
        Ok, then I want to understand your use case a little better. I'm unclear as to why you would want to use both the J2eePreAuthenticatedProcessingFilter and ChannelSet.login() in combination. Is it because you have non-flex portions of the application as well? Maybe I'm missing something. Can you provide more detail about the end result you would like to have?

        Comment


        • #5
          The requirement I have is to use pre-authentication to auto-login users. In addition, the user must be able to login as a different user (i.e. the auto-logged-in user should be able to sign off, be presented a login form, and submit credentials for a different user). This sort of requirement is easily fulfilled with plain Spring Security in a traditional web app. In fact, I have it currently working in a Flex RIA using Flex+HttpService and Spring Security although it's a bit kludgy (it doesn't use BlazeDS security at all).

          I am now trying to follow the "Flex & Spring Integration" refcard as a pattern. So, my Flex login form is now attempting to use ChannelSet.login for processing the credentials instead of submitting them through Flex's HttpService. Maybe I'm attempting something unorthodox here...I thought that ChannelSet.login was the Blaze-sanctioned way of authentication instead of an HTTP POST to /j_spring_security_check.

          I don't have any non-Flex portions of the application.

          Comment


          • #6
            ChannelSet.login() is indeed the "Blaze-sanctioned" way to authenticate. I wouldn't say that what you're trying to do is unorthodox...it actually sounds fairly typical. But what I'm still trying to understand about your particular scenario is how are you obtaining the user's credentials for the auto-login? And are trying to use ChannelSet.login() to authenticate at that time?

            Comment


            • #7
              how are you obtaining the user's credentials for the auto-login?
              Technically speaking, I'm not obtaining the credentials for auto-login.

              I have IIS configured with the ISAPI redirector sitting in front of Apache Tomcat. When IE is the browser, the Spring Security J2eePreAuthenticatedProcessingFilter will grab the AUTHORIZATION header put there by IIS, and populate the SpringSecurityContext with the user's authentication token. Here is some of my spring xml.

              PHP Code:
              <security:http auto-config="false" session-fixation-protection="none" >
                  <
              security:intercept-url pattern="/messagebroker/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
                  <
              security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
                  <
              security:form-login />
                  <
              security:logout logout-url="/j_spring_security_logout"/>
              </
              security:http>

              <!-- 
              Configuration for pre-authentication (e.gUser logged into Windows IE IIS Tomcat) -->
              <
              bean id="jeePreAuthenticatedFilter" 
                  
              class="org.springframework.security.ui.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
                  <
              security:custom-filter position="PRE_AUTH_FILTER"/>
                  <
              property name="authenticationManager" ref="authenticationManager" />
              </
              bean>
              <
              bean id="preAuthAuthenticationProvider" 
                  
              class="org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider">
                  <
              security:custom-authentication-provider/>
                  <
              property name="preAuthenticatedUserDetailsService" ref="userDetailsService"/>
              </
              bean

              And are trying to use ChannelSet.login() to authenticate at that time?
              For pre-auth, I am not calling ChannelSet.login(). It is when I want to logout of my pre-authenticated session and login manually as another user that things fall apart. Because the ChannelSet.login() is passing through the Spring Security filters, the pre-auth filter populates the UserPrincipal into the HttpServletRequest again before SpringSecurityLoginCommand.doAuthentication() has a chance to run. In fact, the code in BlazeDS LoginManager.login() circumvents the LoginCommand altogether throwing the "cannot re-auth" error. Here's some of LoginManager code:

              PHP Code:
              /**
                   * Perform login with username and credentials.
                   *
                   * @param username Username to use to login.
                   * @param credentials Credentials to use to login.
                   */
                  
              public void login(String usernameObject credentials)
                  {
                      if (
              getCurrentPrincipal() == null)
                      {
                          if (
              loginCommand != null)
                          {
                              if (
              username != null && credentials != null)
                              {
                                  
              Principal authenticated loginCommand.doAuthentication(usernamecredentials);

                                  if (
              authenticated == null)
                                  {
                                      
              // Invalid login.
                                      
              SecurityException se = new SecurityException();
                                      
              se.setMessage(INVALID_LOGIN);
                                      
              se.setCode(SecurityException.CLIENT_AUTHENTICATION_CODE);
                                      throw 
              se;
                                  }
                                  
              setCurrentPrincipal(authenticated);
                              }
                              else
                              {
                                  
              // Login is required but the client passed null principal and credentials.
                                  
              SecurityException se = new SecurityException();
                                  
              se.setMessage(LOGIN_REQ);
                                  
              se.setCode(SecurityException.CLIENT_AUTHENTICATION_CODE);
                                  throw 
              se;
                              }
                          }
                          else
                          {
                              
              // Client needs to be externally authenticated via Basic Authentication or some other method.
                              
              SecurityException se = new SecurityException();
                              
              se.setMessage(NO_LOGIN_COMMAND);
                              
              se.setCode(SecurityException.SERVER_AUTHENTICATION_CODE);
                              throw 
              se;
                          }
                      }
                      else
                      {
                          
              // It is possible that the username passed in from the client and that stored in the
                          // Principal on the session may be different.  To facilitate this case a LoginCommand
                          // must implement LoginCommandExt and the user stored in the Principal is retrieved
                          // here for comparison
                          
              String comparisonUsername;
                          if(
              loginCommand instanceof LoginCommandExt)
                          {
                              
              comparisonUsername = ((LoginCommandExt)loginCommand).getPrincipalNameFromCredentials(usernamecredentials);
                          }
                          else
                          {
                              
              comparisonUsername username;
                          }

                          
              // If we have a username and a different existing principal then we
                          // must raise an exception as we don't allow re-authentication for
                          // a given session...
                          
              if (comparisonUsername != null && !comparisonUsername.equals(getCurrentPrincipal().getName()))
                          {
                              
              // Cannot re-authenticate in the same session.
                              
              SecurityException se = new SecurityException();
                              
              se.setMessage(CANNOT_REAUTH);
                              
              se.setCode(SecurityException.CLIENT_AUTHENTICATION_CODE);
                              throw 
              se;
                          }
                      }
                  } 
              FYI - I have a destination endpoint Flex calls when the client app is starting up: User getAuthenticatedUser().
              If this call fails to return a User (e.g. the Windows user doesn't have an account in my application), the client app challenges the user with the login form; otherwise the main page of the application is presented.

              I hope I'm getting closer to answering your questions.

              Comment


              • #8
                Right, now we're definitely getting somewhere.

                So the problem isn't entirely that the J2eePreAuthenticatedProcessingFilter is setting the Authentication earlier in the ChannelSet.login() request. The BlazeDS LoginManager fails because it ultimately gets the current user Principal from HttpServletRequest.getUserPrincipal() when it does its comparison against the new username/password.

                In reality, the call to HttpServletRequest.getUserPrincipal() at that point in the request is getting handled by the SecurityContextHolderAwareRequestWrapper instead of actually calling through to the real underlying HttpServletRequest. Because of that, there is a way to make things work by providing a custom MessageInterceptor that resets the Authentication on the SecurityContext when a login command message is being processed.

                Such an implementation might look like this:
                Code:
                package flex.spring.samples;
                
                import org.springframework.flex.core.MessageInterceptor;
                import org.springframework.flex.core.MessageProcessingContext;
                import org.springframework.security.context.SecurityContextHolder;
                
                import flex.messaging.messages.CommandMessage;
                import flex.messaging.messages.Message;
                
                public class SecurityContextCleanupInterceptor implements MessageInterceptor {
                
                    public Message postProcess(MessageProcessingContext context, 
                        Message inputMessage, Message outputMessage) {
                        return outputMessage;
                    }
                
                    public Message preProcess(MessageProcessingContext context, 
                        Message inputMessage) {
                        if (inputMessage instanceof CommandMessage) {
                            CommandMessage command = (CommandMessage) inputMessage;
                            if (command.getOperation() == CommandMessage.LOGIN_OPERATION) {
                                SecurityContextHolder.getContext().setAuthentication(null);
                            }
                        }
                        return inputMessage;
                    }
                }
                You can then integrate this into the AMF message processing chain using the namespace config...this would look something like the following:

                Code:
                <flex:message-broker>
                    <flex:message-interceptor ref="securityCleanupInterceptor"/>
                    <flex:secured />
                </flex:message-broker>
                	
                <bean id="securityCleanupInterceptor" 
                    class="flex.spring.samples.SecurityContextCleanupInterceptor"/>

                Comment


                • #9
                  Fantastic! That works. I really appreciate your help.

                  Adam

                  Comment


                  • #10
                    I'm having a hell of a time getting this to work. I'm actually allowing Flex to override the pre-authenticated user and manually login using a different user for my Flexunit tests. This is all for integration testing to test my method-level security layer on my remote object. We have Siteminder enabled as our pre-authentication provider, but in testing scenarios, siteminder is unavailable. Furthermore we want administrators to be able to override the current user.

                    I've installed the method interceptor as shown, and I've verified that it runs for every flex call to ChannelSet.login(). It properly clears the security context. I also installed a CustomLoginCommand so that I could debug and log those commands.

                    The problem I'm seeing is that there seems to be no order between the message interceptor and then the login command. Occasionally I've seen this work correctly, where the context is cleared and then the security system processes the login command. More often, the system ignores the login command because it is already pre-authenticated (the interceptor has not cleared the context yet), and then the clear is performed. This invalidates all of my subsequent calls to my secured remote service.

                    flex-servlet.xml
                    Code:
                    <flex:message-broker>
                    	<flex:config-processor ref="customLoginCommand" />
                    	<flex:message-interceptor ref="securityCleanupInterceptor" />		
                    	<flex:secured />
                    </flex:message-broker>
                    and a simple flexunit test.

                    Code:
                    channelSet.login("user","password");
                    remoteObject.getRequest(-1);
                    I receive a "org.springframework.security.AuthenticationCreden tialsNotFoundException" because the login command was ignored. I hope you guys can help me with this because I don't see any other examples out there.

                    Comment

                    Working...
                    X