Using another Crowd-Spring integration topic I have managed to get Crowd and Spring Security 4 integration working. But I fail to get SSO working. It seems that the application doesn't receive the authentication token from Crowd.
The code I use is this:
@Configuration @EnableWebSecurity(debug = false) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private CrowdSSOAuthenticationProcessingFilter filter; @Override protected void configure( HttpSecurity http ) throws Exception { http .authenticationProvider( crowdAuthenticationProvider() ) .addFilter( crowdSSOAuthenticationProcessingFilter() ) .authorizeRequests() .antMatchers( "/", "/home" ).permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage( "/login" ) .permitAll() .and() .logout() .permitAll(); } @Override protected void configure( AuthenticationManagerBuilder auth ) throws Exception { auth.authenticationProvider( crowdAuthenticationProvider() ); } @Bean public CrowdSSOAuthenticationProcessingFilter crowdSSOAuthenticationProcessingFilter() throws Exception { filter = new CrowdSSOAuthenticationProcessingFilter(); filter.setHttpAuthenticator( httpAuthenticator() ); filter.setAuthenticationManager( authenticationManager() ); filter.setAuthenticationFailureHandler( authenticationFailureHandler() ); filter.setAuthenticationSuccessHandler( authenticationSuccessHandler() ); filter.setFilterProcessesUrl( "/j_security_check" ); filter.setUsernameParameter( "j_username" ); filter.setPasswordParameter( "j_password" ); return filter; } @Bean public AuthenticationFailureHandler authenticationFailureHandler() { UsernameStoringAuthenticationFailureHandler failureHandler = new UsernameStoringAuthenticationFailureHandler(); failureHandler.setDefaultFailureUrl( "/login?error=true" ); return failureHandler; } @Bean public AuthenticationSuccessHandler authenticationSuccessHandler() { SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); successHandler.setDefaultTargetUrl( "/home" ); return successHandler; } @Bean CrowdAuthenticationProvider crowdAuthenticationProvider() throws IOException { return new RemoteCrowdAuthenticationProvider( crowdAuthenticationManager(), httpAuthenticator(), crowdUserDetailsService() ); } @Bean public CrowdUserDetailsService crowdUserDetailsService() throws IOException { CrowdUserDetailsServiceImpl crowdUserDetailsService = new CrowdUserDetailsServiceImpl(); crowdUserDetailsService.setUserManager( userManager() ); crowdUserDetailsService.setAuthorityPrefix( "" ); crowdUserDetailsService.setGroupMembershipManager( new CachingGroupMembershipManager( securityServerClient(), userManager(), groupManager(), cache() ) ); return crowdUserDetailsService; } @Bean() public HttpAuthenticator httpAuthenticator() throws IOException { return new HttpAuthenticatorImpl( crowdAuthenticationManager() ); } @Bean public AuthenticationManager crowdAuthenticationManager() throws IOException { return new SimpleAuthenticationManager( securityServerClient() ); } @Bean public GroupManager groupManager() throws IOException { return new CachingGroupManager( securityServerClient(), cache() ); } @Bean public CachingUserManager userManager() throws IOException { return new CachingUserManager( securityServerClient(), cache() ); } @Bean public SecurityServerClient securityServerClient() throws IOException { return new SecurityServerClientImpl( soapClientProperties() ); } @Bean public SoapClientProperties soapClientProperties() throws IOException { Properties prop = new Properties(); try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream( "crowd.properties" )) { prop.load( in ); } return SoapClientPropertiesImpl.newInstanceFromProperties( prop ); } @Bean public BasicCache cache() { return new CacheImpl( Thread.currentThread().getContextClassLoader().getResource( "crowd-ehcache.xml" ) ); } }
Login form is this:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example </title> </head> <body> <div th:if="${param.error}"> Invalid username and password. </div> <div th:if="${param.logout}"> You have been logged out. </div> <form th:action="@{/j_security_check}" method="post"> <div><label> User Name : <input type="text" name="j_username"/> </label></div> <div><label> Password: <input type="password" name="j_password"/> </label></div> <div><input type="submit" value="Sign In"/></div> </form> </body> </html>
Authentication succeeds
12:02:59.193 [http-nio-8080-exec-3] DEBUG c.a.c.i.s.CrowdSSOAuthenticationProcessingFilter - Authentication success. Updating SecurityContextHolder to contain: com.atlassian.crowd.integration.springsecurity.CrowdSSOAuthenticationToken@35684235...
But going through the filter chain
CrowdHttpTokenHelperImpl - Unable to find a valid Crowd token.
and later AnonymousAuthenticationFilter populates the SecurityContextHolder with anonymous token.
I had it all working with Spring Security 3, the SSO stuff too. The crowd.properies file and ehcache.xml files are exactly the same. But with Spring 4 I have no clue where to search for the problem. My last efforts used Crowd libraries 2.7.1, but I had no better luck with 2.8.3 or 2.9.1.
Can someone point me out what is wrong with my code.
Community moderators have prevented the ability to post new answers.
My initial solution appears to work as expected – SSO cookie is created.
public class MySSOAuthenticationProcessingFilter extends CrowdSSOAuthenticationProcessingFilter { private static final Logger logger = LoggerFactory.getLogger( MySSOAuthenticationProcessingFilter.class ); private HttpAuthenticator httpAuthenticator; @Override protected void successfulAuthentication( HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult ) throws IOException, ServletException { storeTokenIfCrowd( request, response, authResult ); super.successfulAuthentication( request, response, chain, authResult ); } public void setHttpAuthenticator( HttpAuthenticator httpAuthenticator ) { this.httpAuthenticator = httpAuthenticator; super.setHttpAuthenticator( httpAuthenticator ); } private void storeTokenIfCrowd( HttpServletRequest request, HttpServletResponse response, Authentication authResult ) { if ( authResult instanceof CrowdSSOAuthenticationToken && authResult.getCredentials() != null ) { try { httpAuthenticator.setPrincipalToken( request, response, authResult.getCredentials().toString() ); } catch ( Exception var5 ) { logger.error( "Unable to set Crowd SSO token", var5 ); } } } }
Looks like the root cause of my problem is that CrowdSSOAuthenticationProcessingFilter has an overridden method
successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult)
Spring Security 3 had it this way but Spring Security4 expects the method to additionally contain FilterChain as a third parameter and therefore the method in CrowdSSOAuthenticationProcessingFilter is never called and the token is never saved into a cookie.
The solution is to define my own filter class and override the relevant methods.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.