How to configure waffle in spring using java configuration

I'm struggling to get waffle to work with spring 4.2.5 using spring java configuration. And I thought I could help others in the same situation.

We use the user preWaffle and postWaffle filters to verify that the user exists in our database after it has been verified using the WLLL NTLM protocol.

We also have authorization methods for user actions using the EnableGlobalMethodSecurity annotation.

To get this working in the spring java configuration, there was little to say the least. You can find our solution in the answer below. Hope this helps.

+8
java spring configuration waffle
source share
1 answer

SpringConfiguration.java

// ... imports @Configuration @EnableWebMvc @EnableScheduling @PropertySources({ @PropertySource("classpath:app.properties") // ... Properties sources }) @EnableTransactionManagement @ComponentScan(basePackages = "com.our.package") public class SpringConfiguration extends WebMvcConfigurerAdapter { // Our Spring configuration ... } 

SecurityConfiguration.java

 // ... imports @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true) @Order(1) public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ // Authentication manager configuration @Autowired private WindowsAuthenticationProviderWrapper authProvider; @Autowired private AuthenticationManagerBuilder auth; @Override protected void configure(final AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authProvider); } @Bean public AuthenticationManager authenticationManager() throws Exception { return auth.getObject(); } // Waffle configuration @Bean public Filter customPreAuthSecurityFilter() { return new CustomPreAuthSecurityFilter(); } @Bean public Filter customNegotiateSecurityFilter() { return new CustomNegotiateSecurityFilter(); } @Bean public WindowsAuthProviderImpl waffleAuthProvider(){ return new WindowsAuthProviderImpl(); } @Bean(name="negotiateSecurityFilterProvider") @Autowired public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(){ NegotiateSecurityFilterProvider bean = new NegotiateSecurityFilterProvider(waffleAuthProvider()); List<String> protocols = new ArrayList<>(); protocols.add("Negotiate"); bean.setProtocols(protocols); return bean; } @Bean public BasicSecurityFilterProvider basicSecurityFilterProvider(){ return new BasicSecurityFilterProvider(waffleAuthProvider()); } @Bean(name="waffleSecurityFilterProviderCollection") @Autowired public waffle.servlet.spi.SecurityFilterProviderCollection negotiateSecurityFilterProviderCollection() { final List<SecurityFilterProvider> lsp = new ArrayList<>(); lsp.add(negotiateSecurityFilterProvider()); lsp.add(basicSecurityFilterProvider()); return new waffle.servlet.spi.SecurityFilterProviderCollection(lsp.toArray(new SecurityFilterProvider[]{})); } @Bean(name="negotiateSecurityFilterEntryPoint") @Autowired public waffle.spring.NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint() { final waffle.spring.NegotiateSecurityFilterEntryPoint ep = new waffle.spring.NegotiateSecurityFilterEntryPoint(); ep.setProvider(negotiateSecurityFilterProviderCollection()); return ep; } @Bean(name="negotiateSecurityFilter") @Autowired public waffle.spring.NegotiateSecurityFilter waffleNegotiateSecurityFilter(){ waffle.spring.NegotiateSecurityFilter bean = new waffle.spring.NegotiateSecurityFilter(); bean.setRoleFormat("both"); bean.setPrincipalFormat("fqn"); bean.setAllowGuestLogin(false); bean.setProvider(negotiateSecurityFilterProviderCollection()); return bean; } // Static Mappings @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/assets/**"); } // Security filter chain // The custom filters can be removed if you only use waffle // but this is how we added them @Override protected void configure(HttpSecurity http) throws Exception { // A user needs to have the role user and has to be authenticated http.exceptionHandling() .authenticationEntryPoint(negotiateSecurityFilterEntryPoint()).and() .addFilterBefore(customPreAuthSecurityFilter(), BasicAuthenticationFilter.class) .addFilterAfter(waffleNegotiateSecurityFilter(), BasicAuthenticationFilter.class) .addFilterAfter(customNegotiateSecurityFilter(), BasicAuthenticationFilter.class) .authorizeRequests().anyRequest().fullyAuthenticated(); } } 

To be able to autodetect authProvider waffle, I created the following wrapper class.

WindowsAuthenticationProviderWrapper.java

 // ... imports // This class purpose is only to make the Windows authentication provider autowireable in spring. @Component public class WindowsAuthenticationProviderWrapper extends WindowsAuthenticationProvider{} 

As requested (Some code has been deleted due to security risks).

CustomPreAuthFilter.java

 import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * This filter removes the excess negoatiate header sent by IE. If the client * has already authenticated, strip the Authorization header from the request. */ public class CustomPreAuthSecurityFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { SecurityContext sec = SecurityContextHolder.getContext(); HttpServletRequest req = (HttpServletRequest) servletRequest; if(sec != null && sec.getAuthentication() != null) { req = new CustomServletRequestWrapper(req); } try { filterChain.doFilter(req, servletResponse); } catch (RuntimeException e) { sendUnauthorized((HttpServletResponse) servletResponse); } } private void sendUnauthorized(HttpServletResponse response) throws IOException { logger.warn("error logging in user"); SecurityContextHolder.clearContext(); response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } } 

CustomNegotiateSecurityFilter.java

 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.GenericFilterBean; import waffle.servlet.WindowsPrincipal; import waffle.spring.WindowsAuthenticationToken; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Date; /** * Handle post NTLM authentication against system database */ public class CustomNegotiateSecurityFilter extends GenericFilterBean { @Autowired private UserDAO userDAO; @Autowired Environment env; private static final Logger LOGGER = LoggerFactory.getLogger(CustomNegotiateSecurityFilter.class); public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) res; SecurityContext sec = SecurityContextHolder.getContext(); Authentication authentication = sec.getAuthentication(); // Continue filter chain if we are anonymously authenticated or if DB authentication has already happened. if (authentication != null && authentication.getClass() == WindowsAuthenticationToken.class) { // The user is Authenticated with NTLM but needs to be checked against the DB. User user; try { // fetch user from DB ... } catch (Exception e) { // The could not be found in the DB. sendUnauthorized(response); return; } // The user was found in the DB. WindowsPrincipal principal = (WindowsPrincipal)authentication.getPrincipal(); final CustomAuthenticationToken token = new CustomAuthenticationToken(principal); // This class extends WindowsAuthenticationToken // add roles to token ... sec.setAuthentication(token); } chain.doFilter(request, response); } private void sendUnauthorized(HttpServletResponse response) throws IOException { logger.warn("Could not log in user"); SecurityContextHolder.clearContext(); response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } private void addRoleToAuthentication(WindowsAuthenticationToken authentication, String role) { for(GrantedAuthority authority : authentication.getAuthorities()) { if(authority.getAuthority().equals(role)) { return; } } authentication.getAuthorities().add(new SimpleGrantedAuthority(role)); } } 

EDIT

For those who asked the question, there is one implementation. CustomServletRequestWrapper:

 class CustomServletRequestWrapper extends HttpServletRequestWrapper { public CustomServletRequestWrapper(HttpServletRequest request) { super(request); } public String getHeader(String name) { if(name.equals("Authorization")) return null; String header = super.getHeader(name); return (header != null) ? header : super.getParameter(name); // Note: you can't use getParameterValues() here. } public Enumeration getHeaderNames() { List<String> names = Collections.list(super.getHeaderNames()); names.addAll(Collections.list(super.getParameterNames())); names.remove("Authorization"); return Collections.enumeration(names); } } 

If you need more information, do not ask.

+8
source share

All Articles