Announcement Announcement Module
Collapse
No announcement yet.
using authentication token during authorisation Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • using authentication token during authorisation

    I implemented my own AuthenticationProcessingFilter in which I set the authentication token to an instance of DomainSecurityAuthenticationToken, which is a class derived from UsernamePasswordAuthenticationToken.

    DomainSecurityAuthenticationToken has an extra property "domain".

    Using the custom filter with the new token class works fine.

    Now I want to use the same token class (and the extra property "domain") during authorisation.

    In my own Voter (I'm using spring security 2.x) I added the following lines in the vote() method:

    DomainSecurityAuthenticationToken token = (DomainSecurityAuthenticationToken) authentication;

    At run-time I get the following error on this line:

    java.lang.ClassCastException: org.springframework.security.providers.UsernamePas swordAuthenticationToken cannot be cast to be.vlaamsbrabant.domainsecuritytest.security.Domai nSecurityAuthenticationToken
    be.vlaamsbrabant.domainsecuritytest.security.UrlVo ter.vote(UrlVoter.java:18)


    How can I use the same token for authorisation that I used for authentication?
    Do I again have to replace a filter? Which one?

  • #2
    If you are returning a DomainSecurityAuthenticationToken from the AuthenticationProvider and/or setting it in the SecurityContext then that should be what is presented to your voter.

    Are you actually setting the security context contents?

    Comment


    • #3
      Are you certain that your filter is being used to authenticate? Did you verify using the debugger or logging that your Authentication is being used? It sounds like this might be an issue with your configuration and/or custom implementation. Can you post your Spring Security configuration and custom code (please use the code tags)?

      Comment


      • #4
        using authentication token during authorisation

        I posted what I did for the custom authentication process in a long post on Feb 11th of this year in post with the title "extra login fields".

        What happens with the custom authentication token is the following:

        In the custom Authentication Processing Filter:

        Code:
        public Authentication attemptAuthentication (HttpServletRequest request) throws AuthenticationException
        {
        ...
        String domain = request.getParameter (DOMAIN_PARAM_KEY);
        DomainSecurityAuthenticationToken domsecToken = new DomainSecurityAuthenticationToken (username, password, domain);
        ...
        Authentication auth = this.getAuthenticationManager ().authenticate (domsecToken);
        ...
        }
        The auth object is not passed or returned to anything. Do I have to put it in the SecurityContext, so that I can retrieve later on?

        How can I retrieve the SecurityContext in a Voter?

        Comment


        • #5
          Originally posted by guythomas View Post
          The auth object is not passed or returned to anything. Do I have to put it in the SecurityContext, so that I can retrieve later on?
          You should be returning the custom authentication in the filter. AbstractProcessingFilter will add this to the SecurityContextHolder.

          Originally posted by guythomas View Post
          How can I retrieve the SecurityContext in a Voter?
          You get the authentication passed into the Voter. That Authentication object should be the one that you returned in your custom Filter and was set in the SecurityContextHolder.

          If you still have problems I would recommend turning on debug logging for Spring Security. You will likely see logs that contain something like:

          Code:
          Updated SecurityContextHolder to contain the following Authentication:

          Comment


          • #6
            using authentication token during authorisation

            My implementation of the attemptAuthentication() method of the AuthenticationProcessingFilter:

            Code:
            public class CustomAuthenticationProcessingFilter extends AuthenticationProcessingFilter
            {
               @Override
               public Authentication attemptAuthentication (HttpServletRequest request) throws AuthenticationException
               {
                  logger.debug ("CustomAuthenticationProcessingFilter::attemptAuthentication()");
            
                  try
                  {
            	     ...
                     logger.debug ("username : " + username);
                     logger.debug ("password : " + password);
                     logger.debug ("domain : " + domain);
                     UsernamePasswordAuthenticationToken authRequest = new DomainSecurityAuthenticationToken (username, password, domain);
            
                     // Place the last username attempted into HttpSession for views
                     HttpSession session = request.getSession (false);
                     if (session != null || getAllowSessionCreation ()) request.getSession ().setAttribute (SPRING_SECURITY_LAST_USERNAME_KEY, ...);
            
                     // Allow subclasses to set the "details" property
                     setDetails (request, authRequest);
            
                     // set default target url based on domain in request
                     setDefaultTargetUrl ("/domain/secure/" + domain + "/hoofdpagina");
            
                     return this.getAuthenticationManager ().authenticate (authRequest);
                  }
                  catch (AuthenticationException ae)
                  {
                     logger.error (ae);
                     throw ae;
                  }
               }
            }
            A couple of questions about this implementation:
            - Do I need the code related to setting the session attribute?
            - Do I need the setDetails() call?

            I want to keep my implementation as lean as possible.

            In the logging I see that this implementation is called. These are all the lines related to what I'm trying to implement:

            Code:
            CustomAuthenticationProcessingFilter:251 - Request is to process authentication
            CustomAuthenticationProcessingFilter:35 - CustomAuthenticationProcessingFilter::attemptAuthentication()
            CustomAuthenticationProcessingFilter:42 - username : aaa
            CustomAuthenticationProcessingFilter:44 - domain : provraad
            CustomUserDetailsAuthenticationProvider:34 - CustomUserDetailsAuthenticationProvider::supports()
            CustomUserDetailsAuthenticationProvider:35 - auth.class : be.vlaamsbrabant.domainsecuritytest.security.DomainSecurityAuthenticationToken
            CustomUserDetailsAuthenticationProvider:38 - supports : true
            CustomUserDetailsAuthenticationProvider:52 - Retrieving the logged in user details ...
            CustomUserDetailsAuthenticationProvider:60 - USERNAME: aaa
            CustomUserDetailsAuthenticationProvider:72 - Retrieve user:
            CustomUserDetailsAuthenticationProvider:73 - - Naam    : aaa
            CustomUserDetailsAuthenticationProvider:79 - DOMAIN: provraad
            DomainSecurityDAOImpl:50 - DomainSecurityDAOImpl::DomainSecurityDAOImpl()
            DomainSecurityDAOImpl:318 - wsdlLocationURL : http://localhost:8084/domainsecurity-ws/DomainSecurityService?wsdl
            CustomUserDetailsAuthenticationProvider:116 - additionalAuthenticationChecks()
            CustomUserDetailsAuthenticationProvider:119 - secuserDomain : provraad
            CustomUserDetailsAuthenticationProvider:121 - tokenDomain : provraad
            CustomUserDetailsAuthenticationProvider:139 - createSuccessAuthentication()
            CustomAuthenticationProcessingFilter:363 - Updated SecurityContextHolder to contain the following Authentication: 'be.vlaamsbrabant.domainsecuritytest.security.DomainSecurityAuthenticationToken@4b47bf7b: Principal: [email protected]16a7c99; Password: [PROTECTED]; Authenticated: true; Details: org.springframework.security.ui.WebAuthenticationDetails@1c07a: RemoteIpAddress: 127.0.0.1; SessionId: 6322CADEFA5EBC7164638F5F5B76A21D; Granted Authorities: ROLE_DOMAIN_USER'
            UrlVoter:17 - UrlVoter::vote()
            UrlVoter:20 - auth.class : be.vlaamsbrabant.domainsecuritytest.security.DomainSecurityAuthenticationToken
            UrlVoter:24 - domain : provraad
            Next, as shown in the logging my own implementation of the AbstractUserDetailsAuthenticationProvider is called (CustomUserDetailsAuthenticationProvider)

            I implemented the supports() method and in the logging you can see that the implementation class logged in that method is my own class (DomainSecurityAuthenticationToken, derived from UsernamePasswordAuthenticationToken).

            Next my own implementation of retrieveUser() of the CustomUserDetailsAuthenticationProvider is called:

            Code:
            @Override
               protected UserDetails retrieveUser (String username, UsernamePasswordAuthenticationToken token) throws AuthenticationException
               {
                  if (logger.isInfoEnabled ()) logger.info ("Retrieving the logged in user details ...");
            
                  username = token.getPrincipal ().toString ();
                  logger.debug ("USERNAME: " + username);
            
                  String paswoord = token.getCredentials ().toString ();
            
                  if (logger.isDebugEnabled ()) logger.debug ("Retrieve user:");
                  if (logger.isDebugEnabled ()) logger.debug ("- Naam    : " + username);
            
                  String domain = ((DomainSecurityAuthenticationToken) token).getDomain ();
                  logger.debug ("DOMAIN: " + domain);
            
                  // getting the DomainUser object for specified username, (encoded) password and domain
                  DomainSecurityClient domsecClient = DomainSecurityClientFactory.getInstance ();
                  DomainUser domainUser = null;
                  UserDetails secUserDetails = null;
                  try
                  {
                     secUserDetails = ...
                  }
                  catch (DomainSecurityException dse)
                  {
                     logger.error (dse);
                  }
                  catch (AuthenticationException ae)
                  {
                     logger.error (ae);
                     throw ae;
                  }
            
                  return secUserDetails;
               }
            Next my own implementations of additionalAuthenticationChecks() and createSuccessAuthentication() in the AuthenticationProvider are called.

            This is my implementation of createSuccessAuthentication():

            Code:
               protected Authentication createSuccessAuthentication (Object principal, Authentication authentication, UserDetails user)
               {
                  logger.debug ("createSuccessAuthentication()");
            
                  DomainSecurityAuthenticationToken result = new DomainSecurityAuthenticationToken (principal,
                        authentication.getCredentials (), user.getAuthorities (), ((DomainSecurityAuthenticationToken) authentication)
                              .getDomain ());
                  result.setDetails (authentication.getDetails ());
            
                  return result;
               }
            After adding this implementation I got the lines in my logging showing that the DomainSecurityAuthenticationToken was stored in the SecurityContextHolder and my additional Voter works.

            WHEN I LEAVE OUT THE IMPLEMENTATION OF createSuccessAuthentication () I get the following log lines. Now UsernamePasswordAuthenticationToken is stored in the SecurityContextHolder and I get the ClassCastException in the UrlVoter.

            Code:
            CustomAuthenticationProcessingFilter:363 - Updated SecurityContextHolder to contain the following Authentication: 'org.springframework.security.providers.UsernamePasswordAuthenticationToken@b5a59201: Principal: [email protected]76fb1b; Password: [PROTECTED]; Authenticated: true; Details: [email protected]: RemoteIpAddress: 127.0.0.1; SessionId: BD2C174501CDE769E206CD51B7B5D7F0; Granted Authorities: ROLE_DOMAIN_USER'
            UrlVoter:17 - UrlVoter::vote()
            UrlVoter:20 - auth.class : org.springframework.security.providers.UsernamePasswordAuthenticationToken
            [default]:260 - Servlet.service() for servlet default threw exception
            java.lang.ClassCastException: org.springframework.security.providers.UsernamePasswordAuthenticationToken cannot be cast to be.vlaamsbrabant.domainsecuritytest.security.DomainSecurityAuthenticationToken
            I downloaded the source code and what happens according to me is this (correct me if I'm wrong):

            At a certain point the authenticate() method of AbstractUserDetailsAuthenticationProvider is called. In this method retrieveUser of my own implementation is called:

            Code:
            try {
                            user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
                        } catch (UsernameNotFoundException notFound) { ...
                        }
            authenticate() returns the result of createSuccessAuthentication(). In this method an instance of UsernamePasswordAuthenticationToken is created:

            Code:
            protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
                    UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
                            authentication.getCredentials(), user.getAuthorities());
                    result.setDetails(authentication.getDetails());
                    return result;
                }
            Apparently this instance is stored in the SecurityContextHolder and not my own implementation of this token, even when I end my implementation of AuthenticationProcessingFilter attempAuthentication() with the following line:

            Code:
            return this.getAuthenticationManager ().authenticate (authRequest);
            authRequest has been instantiated in the same method as follows:

            Code:
            UsernamePasswordAuthenticationToken authRequest = new DomainSecurityAuthenticationToken (username, password, domain);

            Comment


            • #7
              The AuthenticationProvider calls createSuccessAuthentication() and returns the instance provided by that method.

              So that is the value that is returned by the AuthenticationManager and ultimately stored in the security context. If you don't customize the returned object, you will get a basic UsernamePasswordAuthenticationToken, as provided by the default implementation of createSuccessAuthentication().

              Comment


              • #8
                using authentication token during authorisation

                Hallo Luke, if I understand you correctly, implementing my own version of createSuccessAuthentication() was the right thing to do?

                But I'm still wondering why the authentication object in the retrieveUser() implementation of AbstractUserDetailsAuthenticationProvider is cast to UsernamePasswordAuthenticationToken.

                If this weren't the case, couldn't we do without providing our own version of createSuccessAuthentication()?

                In the attemptAuthentication method of the AuthenticationProcessingFilter I already initialized UsernamePasswordAuthenticationToken as a reference to a DomainSecurityAuthenticationToken:

                Code:
                UsernamePasswordAuthenticationToken authRequest = new DomainSecurityAuthenticationToken (username, password, domain);
                which is then passed to the authentication manager:

                Code:
                return this.getAuthenticationManager ().authenticate (authRequest);
                It looks to me as if this reference is lost when we do not provide our own implementation of createSuccessAuthentication().

                Or am I talking complete nonsense?

                Comment


                • #9
                  Sorry, the second sentence in my previous mail should be:

                  But I'm still wondering why the authentication object in the autheticate() method of AbstractUserDetailsAuthenticationProvider is cast to UsernamePasswordAuthenticationToken.

                  Code:
                  user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);

                  Comment


                  • #10
                    Originally posted by guythomas View Post
                    Sorry, the second sentence in my previous mail should be:

                    But I'm still wondering why the authentication object in the autheticate() method of AbstractUserDetailsAuthenticationProvider is cast to UsernamePasswordAuthenticationToken.
                    It's cast because DaoAuthenticationProvider is for authentication using a username/password and the retrieveUser signature requires a UsernamePasswordAuthenticationToken. It shouldn't make any difference - casting doesn't affect the type of the object.

                    It seems like you are getting confused by the type of the authentication request. Even though the signature of AuthenticationProvider is

                    Code:
                    Authentication authenticate(Authentication authRequest)
                    the returned object which is the result of the authentication process is a new instance and is not connected with the request object (other than indirectly). The request object is discarded.

                    It might be more obvious if you just try implementing AuthenticationProvider directly rather than trying to understand the process as it is implemented in DaoAuthenticationProvider. Inheritance tends to confuse the picture.

                    Comment


                    • #11
                      That's what I suspected (but wasn't sure about): that the request object is discarded and a new authentication object is created.

                      Thanks for your comments.

                      Another question I asked before: in my own implementation of attemptAuthentication() I took over the following lines of code:

                      Code:
                      // Place the last username attempted into HttpSession for views
                               HttpSession session = request.getSession (false);
                               if (session != null || getAllowSessionCreation ())
                               {
                                  request.getSession ().setAttribute (SPRING_SECURITY_LAST_USERNAME_KEY, TextUtils.escapeEntities (username));
                               }
                      
                               // Allow subclasses to set the "details" property
                               setDetails (request, authRequest);
                      Are they needed or can I remove them?

                      Comment


                      • #12
                        It depends on whether you want access to the username (in a failed authentication view, for example) or the "details" object created by the authentication filter for any reason. If you don't, then you don't need them.

                        Comment

                        Working...
                        X