I am using Spring Security combined with Waffle to authenticate the users of my webapp. I configured Spring Security with the following configuration:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import waffle.servlet.spi.BasicSecurityFilterProvider;
import waffle.servlet.spi.NegotiateSecurityFilterProvider;
import waffle.servlet.spi.SecurityFilterProvider;
import waffle.servlet.spi.SecurityFilterProviderCollection;
import waffle.spring.NegotiateSecurityFilter;
import waffle.spring.NegotiateSecurityFilterEntryPoint;
import waffle.windows.auth.impl.WindowsAuthProviderImpl;
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private NegotiateSecurityFilterEntryPoint entryPoint;
@Autowired
private NegotiateSecurityFilter filter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(entryPoint);
http.addFilterBefore(filter, BasicAuthenticationFilter.class).authorizeRequests().anyRequest()
.fullyAuthenticated();
}
@Bean
public WindowsAuthProviderImpl windowsAuthProviderImpl() {
return new WindowsAuthProviderImpl();
}
@Bean
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(final WindowsAuthProviderImpl authProvider) {
return new NegotiateSecurityFilterProvider(authProvider);
}
@Bean
public BasicSecurityFilterProvider basicSecurityFilterProvider(final WindowsAuthProviderImpl authProvider) {
return new BasicSecurityFilterProvider(authProvider);
}
@Bean
public SecurityFilterProviderCollection securityFilterProviderCollection(
final NegotiateSecurityFilterProvider negotiateSecurityFilterProvider,
final BasicSecurityFilterProvider basicSecurityFilterProvider) {
return new SecurityFilterProviderCollection(new SecurityFilterProvider[] { negotiateSecurityFilterProvider,
basicSecurityFilterProvider });
}
@Bean
public NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint(
final SecurityFilterProviderCollection securityFilterProviderCollection) {
final NegotiateSecurityFilterEntryPoint entryPoint = new NegotiateSecurityFilterEntryPoint();
entryPoint.setProvider(securityFilterProviderCollection);
return entryPoint;
}
@Bean
public NegotiateSecurityFilter negotiateSecurityFilter(
final SecurityFilterProviderCollection securityFilterProviderCollection) {
final NegotiateSecurityFilter filter = new NegotiateSecurityFilter();
filter.setProvider(securityFilterProviderCollection);
return filter;
}
}
The authentication process is working as expected, but I can only read the name of the currently logged in user in a controller like this:
@RequestMapping("/")
public @ResponseBody String index(final Principal user) {
return String.format("Welcome to the home page, %s!", user.getName());
}
For the authorization I would like to assign roles to the users that I define in a database and additional information (like email-adresses, phone number, etc.) are stored in Active Directory. How can these information automatically be merged?
I created another filter that is fired after the Waffle filter ran. It checks if Waffle authenticated a user. If so, I create my own Authentication
object with the information from the Waffle object.
Update
In the meantime I went back to form based login. But I think I did it like this:
A custom UserDetails
class:
@Data
public class User implements UserDetails {
private static final long serialVersionUID = -302856598965676658L;
private String username;
private Set<Role> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return "";
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
A custom GrantedAuthority
class:
@Data
public class Role implements GrantedAuthority {
private static final long serialVersionUID = -7912276892872811638L;
private String authority;
@Override
public String getAuthority() {
return authority;
}
}
The custom authentication class:
public class CustomAuthentication implements Authentication {
private static final long serialVersionUID = -1723253799961522167L;
private User user;
public CustomAuthentication(final User user) {
this.user = user;
}
@Override
public String getName() {
return user.getUsername();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return user;
}
@Override
public boolean isAuthenticated() {
return user != null;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
}
The custom filter:
@Component
public class CustomFilter extends GenericFilterBean {
@Autowired
private UserService userService;
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final SecurityContext securityContext = SecurityContextHolder.getContext();
final Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
final String username = authentication.getName();
final User user = userService.getUserByUsername(username);
final CustomAuthentication customAuthentication = new CustomAuthentication(user);
securityContext.setAuthentication(customAuthentication);
}
chain.doFilter(request, response);
}
}
The registration of the filters:
@Configuration
@EnableWebMvcSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private NegotiateSecurityFilter waffleFilter;
@Autowired
private CustomFilter customFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(waffleFilter, BasicAuthenticationFilter.class);
http.addFilterAfter(customFilter, NegotiateSecurityFilter.class);
}
}