Announcement Announcement Module
Collapse
No announcement yet.
Want remember-me to return UserDetails Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Want remember-me to return UserDetails

    I have Spring Security 3.0 working with an Alfresco CMS back-end (which was not easy since Alfresco won't give you back passwords, even hashed ones). I have <remember-me/> working with it too, using the default configuration, but not to the degree I would like.

    My problem is that RememberMe stores the RememberMeAuthenticationToken in the SecurityContext. I prefer that our custom Alfresco UserDetails be stored in the SecurityContext, since it has an Alfresco ticket, which is needed for subsequent calls.

    What's the best way to achieve this? I wouldn't mind putting in a hook just before the the RememberMeAuthenticationToken object is stored in the in the SecurityContext, but, though I read the docs over and over, I don't see how I can do this.

    For UsernamePasswordAuthenticationToken's I solved this problem by creating a custom DaoAuthenticationProvider, overriding createSuccessAuthentication(...) to return the UserDetails instead of the token. Similarly, I tried to configure a RememberMeAuthenticationProvider, but it is being ignored. (Configuration below).

    Here's what I have that's working (though the remember me returns the RememberMeAuthenticationToken, which is not what I want):


    Code:
       
    <!-- HTTP security configurations -->
        <http auto-config="true" use-expressions="true">
            <form-login login-processing-url="/j_spring_security_check" login-page="/login"
                        authentication-failure-url="/login?login_error=t"/>
            <logout logout-url="/j_spring_security_logout"/>
            <remember-me/>
    	<!-- Secure URI configuration -->
            <intercept-url pattern="/foo/blog/post**" 
            <intercept-url pattern="/**" access="permitAll"/>
        </http>
    
    	<beans:bean id="alfrescoAuthenticationProvider"
    	    class="com.bbg.community.services.security.MyAuthenticationProvider">
    	  <beans:property name="userDetailsService" ref="securityService"/>
    	  <beans:property name="passwordEncoder" ref="passwordEncoder"/>
    	</beans:bean>
    
        <authentication-manager alias="authenticationManager">
            <authentication-provider ref='alfrescoAuthenticationProvider'/>
        </authentication-manager>
    Here's what I tried for Remember-Me custom configuration, I just tried the default classes first to try to get it to work (this is just taken from the docs):
    Code:
       
    <beans:bean id="rememberMeFilter" class=
    	 "org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
    	  <beans:property name="rememberMeServices" ref="rememberMeServices"/>
    	  <beans:property name="authenticationManager" ref="authenticationManager" />
    </beans:bean>
    	
    <beans:bean id="rememberMeServices" class=
    	 "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
    	  <beans:property name="userDetailsService" ref="securityService"/>
    	  <beans:property name="key" value="akey"/>
    </beans:bean>
    	
    <beans:bean id="rememberMeAuthenticationProvider" class=
    	 "org.springframework.security.authentication.RememberMeAuthenticationProvider">
    	  <beans:property name="key" value="akey"/>
    </beans:bean>
    Any pointers on the configuration or on how to otherwise intercept what RememberMeAuthenticationProvider is returning is appreciated.

    Thanks,

    Michael

  • #2
    Not really sure what you mean. You can't return a UserDetails object in place of an Authentication instance.

    Also, most AuthenticationProviders (including remember-me) return the UserDetails as the principal property of the Authentication.

    Comment


    • #3
      I should have mentioned - I made my AlfrescoPerson object implement Authentication and UserDetails.

      Maybe I'm going about it the wrong way. What I need is to have my UserDetails/Person object available in the session so that I can display the user's name, get their ticket to send to the back end CMS (so it knows the user), etc.

      I was figuring that if I returned a UserDetails that implemented Authentication, then I'd always have it around in the securitycontext.

      This is a SpringMVC app, would I be better off adding a servlet filter and then looking up my UserDetails again when authentication is finished? It seems like I'm doubling the number of round trips that way.

      Comment


      • #4
        Originally posted by Luke Taylor View Post
        Also, most AuthenticationProviders (including remember-me) return the UserDetails as the principal property of the Authentication.
        I've avoided taking advantage of this fact since my user has to login to actually get a ticket to the back end CMS. If the Authentication's Principal property gets set to the UserDetails returned by loadUserByUsername(username), the user actually isn't logged into the backend system and doesn't have a ticket. Ideally, I would override additionalAuthenticationChecks() in my own DaoAuthenticationProvider and do the login then, throwing an exception if it fails (which is shouldn't since the hashed password is already confirmed). That would give the UserDetails set on the Authentication to have a real ticket to the backend CMS.

        How can I override the RememberMeAuthenticationProvider so that I can use my own child class that overrides additionalAuthenticationChecks() and do the same thing? I tried what was in the doc (10.4.1 TokenBasedRememberMeServices), but didn't get working.

        Thanks,

        Michael

        Comment


        • #5
          I solved the problem by listening for a successful authentication (called on UsernamePassword and RememberMe) and then logging in the user:

          Code:
          <beans:bean id="authenticationListener" 
              class="foo.bar.AuthenticationSuccessListener">
          	  <beans:property name="loginService" ref="loginService"/>
          	  <beans:property name="secretSauce" ref="secretSauce"/>
          </beans:bean>
          
          public class AuthenticationSuccessListener implements
          		ApplicationListener<InteractiveAuthenticationSuccessEvent> {
          	public void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
          		Authentication auth = event.getAuthentication();
          		Person person = (Person)auth.getPrincipal();
                          loginService.login(person);
                  }

          Events are so elegant (says the EventBus guy).

          Comment


          • #6
            Clever solution! Is it ever possible for login to fail? If so, should the user be logged out of Spr Sec?

            Comment


            • #7
              I assume you mean my login() to the backend system because this event only gets fired on successful login to the site.

              Good point - It's always possible that login will fail, but highly unlikely, since the matching of the hashed password already passed successfully, so this is good enough. Because of this, I only use this technique for RememberMe. For UsernamePassword I do the right thing - I override additionalAuthenticationChecks, let the super class check the password, then if that succeeds, login to the CMS straight away. That way if there is a problem, I can throw the exception before authentication finishes. I wanted to to the same thing for RememberMe, but couldn't get it configured right. The documentation sort of drifts of at that point, it should really have a clear XML example.

              If the login does fail for RememberMe, then it's OK for our use case. They will have access to the site, but would be unable to get access to backend content. We can detect that in the UI layer and redirect them to the login page where they can login and get a new ticket.

              Comment

              Working...
              X