I'm trying to setup a customer AuthenticationProvider with Spring Security but not having much luck getting it working. I'm using Java configuration so I'm probably missing something simple but as most the learning material is XML config based, it's not jumping out at me.
This is using Spring v4.0.1.RELEASE but with Spring Security v3.2.2.RELEASE. Version number clash perhaps?
As far as I could tell, all I had to do was create my provider:
public class KBServicesAuthProvider implements AuthenticationProvider { @Autowired private ApplicationConfig applicationConfig; @Autowired private SessionServiceClient sessionServiceClient; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String email = (String) authentication.getPrincipal(); String password = (String) authentication.getCredentials(); try { KBSessionInfo sessionInfo = sessionServiceClient.login(applicationConfig.getKbServicesPresenceId(), email, password); List<GrantedAuthority> grantedRoles = new ArrayList<>(); for (KBRoleMembership role : sessionInfo.getAuthenticatedUser().getRoleMemberships()) { grantedRoles.add(new SimpleGrantedAuthority(role.getRoleId())); } return new UsernamePasswordAuthenticationToken(email, password, grantedRoles); } catch (InvalidSessionException e) { throw new AuthenticationCredentialsNotFoundException("Username or password was not accepted", e); } } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } }
And then setup a class to describe my security setup. This class links in my provider:
@Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired(required = true) SessionServiceClient sessionServiceClient; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/").permitAll().anyRequest().authenticated(); http.formLogin().loginPage("/login").permitAll().and().logout().permitAll(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(getKBServicesAuthenticationProvider()); } @Bean protected AuthenticationProvider getKBServicesAuthenticationProvider() { return new KBServicesAuthProvider(); } }
But I'm not seeing anything in the logs & none of my debug points are being hit. The app acts as it's unsecured (so I can reach various URLs etc. still).
Any ideas on what I should be checking?
Authentication is the mechanism by which callers prove that they are acting on behalf of specific users or systems. Authentication answers the question, "Who are you?" using credentials such as username/password combinations.
Authentication Provider calls User Details service loads the User Details and returns the Authenticated Principal. Authentication Manager returns the Authenticated Object to Authentication Filter and Authentication Filter sets the Authentication object in Security Context .
For example, when authenticating against some external, third party service (such as Crowd) – both the username and the password from the authentication request will be necessary. For these, more advanced scenarios, we'll need to define a custom Authentication Provider:
The Authentication Provider. The standard and most common implementation is the DaoAuthenticationProvider – which retrieves the user details from a simple, read-only user DAO – the UserDetailsService. This User Details Service only has access to the username in order to retrieve the full user entity – and in a large number of scenarios,...
As shown in the spring security architecture diagram, the AuthenticationProvider is the one responsible for the logic of authentication. The AuthenticationManager receives a request from the HTTP filter layer and delegates the responsibility to authenticate the user to the AuthenticationProvider.
Default Authentication Flow As shown in the spring security architecture diagram, the AuthenticationProvider is the one responsible for the logic of authentication. The AuthenticationManager receives a request from the HTTP filter layer and delegates the responsibility to authenticate the user to the AuthenticationProvider.
This might not be the complete answer, as I'm struggling with this a bit myself. I'm using a custom authentication provider and a custom user details service. I see the same behavior as you -- breakpoints get hit in my user details service, but not in my authentication provider. Here is what my entire config class looks like:
@Configuration @EnableWebMvcSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService userDetailsService; @Autowired private CustomAuthenticationProvider customAuthenticationProvider; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Override protected void configure(HttpSecurity http) throws Exception { AuthenticationProvider rememberMeAuthenticationProvider = rememberMeAuthenticationProvider(); TokenBasedRememberMeServices tokenBasedRememberMeServices = tokenBasedRememberMeServices(); List<AuthenticationProvider> authenticationProviders = new ArrayList<AuthenticationProvider>(2); authenticationProviders.add(rememberMeAuthenticationProvider); authenticationProviders.add(customAuthenticationProvider); AuthenticationManager authenticationManager = authenticationManager(authenticationProviders); http .csrf().disable() .headers().disable() .addFilter(new RememberMeAuthenticationFilter(authenticationManager, tokenBasedRememberMeServices)) .rememberMe().rememberMeServices(tokenBasedRememberMeServices) .and() .authorizeRequests() .antMatchers("/js/**", "/css/**", "/img/**", "/login", "/processLogin").permitAll() .antMatchers("/index.jsp", "/index.html", "/index").hasRole("USER") .antMatchers("/admin", "/admin.html", "/admin.jsp", "/js/saic/jswe/admin/**").hasRole("ADMIN") .and() .formLogin().loginProcessingUrl("/processLogin").loginPage("/login").usernameParameter("username").passwordParameter("password").permitAll() .and() .exceptionHandling().accessDeniedPage("/login") .and() .logout().permitAll(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/js/**", "/css/**", "/img/**"); } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder(){ return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(List<AuthenticationProvider> authenticationProviders) { return new ProviderManager(authenticationProviders); } @Bean public TokenBasedRememberMeServices tokenBasedRememberMeServices() { return new TokenBasedRememberMeServices("testKey", userDetailsService); } @Bean public AuthenticationProvider rememberMeAuthenticationProvider() { return new org.springframework.security.authentication.RememberMeAuthenticationProvider("testKey"); } protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } }
I've just discovered that if I specifically add my authentication provider to the HttpSecurity object, my breakpoints start getting hit:
http .csrf().disable() .headers().disable() .authenticationProvider(customAuthenticationProvider)
My goal is to get a BCryptPasswordEncoder working, which does not with this config -- everything returns as bad credentials. Anyway, just thought I'd share.
isAssignableFrom()
instead of ==
or equals
.The problem is with the supports()
method which will always return false.
Change from:
@Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); }
To:
@Override public boolean supports(Class<?> authentication) { return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); }
override fun supports(authentication: Class<*>): Boolean { return UsernamePasswordAuthenticationToken::class.java.isAssignableFrom(authentication) }
Finally the flow would pass through authenticate()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With