Reputation: 8587
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?
Upvotes: 27
Views: 34070
Reputation: 191
In my case, the problem was that I needed to disable CSRF, since I'm using API, not HTML pages:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
Upvotes: 0
Reputation: 1399
Also, make sure you are sending the user and password with correct headers.
Test curl below, and check the class is been invoked
curl -X GET \
http://localhost:8080 \
-H 'Authorization: Basic cmdlbGxtYW5AYnIuaWJtLmNvbTphYmM=' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'cache-control: no-cache'
Upvotes: 0
Reputation: 380
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()
Upvotes: 9
Reputation: 1
I had a similar problem, and this was because I used @Autowire-ed AuthenticationManager
instance, that was built by spring boot and simply not contained my custom AuthenticationProvider
.
After two days of debugging of spring guts I finally realize that this is not same instance, as manager from org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#authenticationManager()
which I configured in my custom WebSecurityConfigurerAdapter
through WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder)
.
Now I simply get this instance from WebSecurityConfigurerAdapter#authenticationManager()
and pass it to my GenericFilterBean
, which is handle my auth logic. Works fine.
Upvotes: 0
Reputation: 952
You forgot the @Autowired
annotation.
@Autowired
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(getKBServicesAuthenticationProvider());
}
Also, you may want to remove the .antMatchers("/").permitAll()
.
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/login").permitAll().and().logout().permitAll();
}
Upvotes: 1
Reputation: 2019
I had same issue (my custom auth provider is not hit) and solved the issue by introducing springSecurityFilterChain, after reading Why is Spring Security working in Tomcat but not when deployed to Weblogic? So my problem was maybe tied specifically with WebServer, but I had the custom auth provider issue also on Tomcat and checked my configuration works now on Tomcat.
I'm using spring boot 1.4.1 version which contains Spring 4.3.3 and Spring Security 4.1.3 and following Traditional deployment
I tested my configuration against Tomcat v9.0 and also WebLogic 12c R2 and checked it worked on both. hope this helpful at least to someone using Tomcat.
Below is my configuration started from main class.
Application.java
public class Application {
public static void main( String[] args ) {
SpringApplication.run(new Class[] {AppConfig.class, Initializer.class, SecurityInitializer.class}, args);
}
}
Initializer.java
public class Initializer extends SpringBootServletInitializer implements WebApplicationInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AppConfig.class);
}
@Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(WebConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("my-servlet", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
}
}
Here the AbstractSecurityWebApplicationInitializer is building the springSecurityFilterChain from onStartup method. I didn't implement any, since I'm trying to use default configuration.
SecurityInitializer.java
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
AppConfig.java
@Configuration
@EnableAutoConfiguration
@EnableScheduling
@EnableMBeanExport
@EnableAsync
@EnableAspectJAutoProxy
@ComponentScan("com.my.package")
public class AppConfig {
}
SecurityConfig.java
@Configuration
@EnableWebSecurity
@ComponentScan("com.my.package")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestfulRemoteAuthenticationProvider restfulRemoteAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(restfulRemoteAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
WebConfig.java
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.my.controller.package")
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public InternalResourceViewResolver internalViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
viewResolver.setOrder(1);
return viewResolver;
}
}
This is my custom auth provider to get authentication info from other component via Restful request
RestfulRemoteAuthenticationProvider.java
@Component
public class RestfulRemoteAuthenticationProvider implements AuthenticationProvider {
@Autowired
private ManagementClientAdapterFactory managementClientAdapterFactory;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// my logic to get and configure authSource which is my environment specific thing, also same for RemoteAuthRequestResult
RemoteAuthRequestResult result = (RemoteAuthRequestResult)authSource.sendRequest();
if(result.isAuthenticated()) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(username, password, grantedAuths);
}
throw new BadCredentialsException("User not found by given credential");
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Upvotes: 3
Reputation: 1712
@EnableWebMvcSecurity
will be deprecated in 4.0 https://jira.spring.io/browse/SEC-2790
You might want to re consider you configuration.
Upvotes: 1
Reputation: 2087
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.
Upvotes: 13
Reputation: 634
Something like should be present in java config
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class HelloMethodSecurityConfig {
}
Upvotes: 1
Reputation: 634
<security:global-method-security pre-post-annotations="enabled"/>
Upvotes: 0