Hi everyone,
I'm trying to implement Kerberos for an application in Spring Boot + Vue.js currently using LDAP Authentication. I managed to set up everything correctly to get this sample working (full tutorial here) on a remote server with a connection using Windows AD. Now I want to implement this in a REST API context so an application with a Vue.js frontend can access it and skip the login page if the Kerberos authentication succeeds.
I tried to implement the following code on my Spring Boot application. My first try was to make it display the name of the user on the web page like on the given example to validate the good functioning of the application. There's no error but I get the following message: User null. Here's the code that I tried to implement:
WebController.java
@GetMapping(value = "/kerberos")
@ResponseBody
public String sayHello(HttpServletRequest req) {
if (req != null) {
LOGGER.info("User " + req.getRemoteUser());
return "Hello " + req.getRemoteUser();
} else {
LOGGER.info("REQ IS NULL");
return "PRINCIPAL IS NULL";
}
}
WebSecurityConfiguration.java
@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
public Environment env;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout()
.permitAll()
.and()
.addFilterBefore(
spnegoAuthenticationProcessingFilter(),
BasicAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider());
}
@Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
return new >ActiveDirectoryLdapAuthenticationProvider(env.getRequiredProperty("custom.ad.domain"), env.getRequiredProperty("spring.ldap.urls"));
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/kerberos");
}
@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter() {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
try {
AuthenticationManager authenticationManager = authenticationManagerBean();
filter.setAuthenticationManager(authenticationManager);
} catch (Exception e) {
e.printStackTrace();
}
return filter;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(env.getRequiredProperty("spring.krb.principal"));
ticketValidator.setKeyTabLocation(new FileSystemResource(env.getRequiredProperty("spring.krb.keytab")));
ticketValidator.setDebug(true);
return ticketValidator;
}
@Bean
public KerberosLdapContextSource kerberosLdapContextSource() throws Exception {
KerberosLdapContextSource contextSource = new KerberosLdapContextSource(env.getRequiredProperty("spring.ldap.urls"));
contextSource.setLoginConfig(loginConfig());
return contextSource;
}
public SunJaasKrb5LoginConfig loginConfig() throws Exception {
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
loginConfig.setKeyTabLocation(new FileSystemResource(env.getRequiredProperty("spring.krb.keytab")));
loginConfig.setServicePrincipal(env.getRequiredProperty("spring.krb.principal"));
loginConfig.setDebug(true);
loginConfig.setIsInitiator(true);
loginConfig.afterPropertiesSet();
return loginConfig;
}
@Bean
public LdapUserDetailsService ldapUserDetailsService() throws Exception {
FilterBasedLdapUserSearch userSearch =
new FilterBasedLdapUserSearch(env.getRequiredProperty("custom.ldap.base"), env.getRequiredProperty("custom.ldap.filter"), kerberosLdapContextSource());
LdapUserDetailsService service =
new LdapUserDetailsService(userSearch, new ActiveDirectoryLdapAuthoritiesPopulator());
service.setUserDetailsMapper(new LdapUserDetailsMapper());
return service;
}
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() throws Exception {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(ldapUserDetailsService());
return provider;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Is there a way to implement the behaviour with Kerberos and a REST API? Or the method I tried to implement is only working with Java Servlets?
by having in your security configuration those lines:
you opened any of your endpoints to anyone. Including your test endpoint "/kerberos". You got username "null" because Kerberos didn't authenticate anyone. Try being more specific in your opened endpoints, then you can investigate in debug mode why your request was not validated against LDAP The finish line is that once your request sender will be validated via Kreberos you'll get your username as you expected.