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?
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?
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: be.vlaamsbrabant.domainsecuritytest.security.SecurityUserDetails@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: be.vlaamsbrabant.domainsecuritytest.security.SecurityUserDetails@76fb1b; Password: [PROTECTED]; Authenticated: true; Details: org.springframework.security.ui.WebAuthenticationDetails@ffff6a82: 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);
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?