I am attempting to write Spring MVC integration test with Spring Security and Thymeleaf for the view layer.
I have setup my MockMvc object with Spring Security Integration just like all the examples from the documentation.
Integration Test setUp:
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
//.defaultRequest(get("/").with(user(someUser)))
.apply(springSecurity())
.build();
}
Thymeleaf is configured to utilize the SpringSecurityDialect. (thymeleaf-extras-springsecurity4)
additionalDialects.add( new SpringSecurityDialect());
For the purpose of being able to utilize spring security expressions in the view layer (example).
<p sec:authorize="hasRole('ROLE_USER')"> User logged in</p>
Now my configuration works perfectly fine outside of testing however, when I try to make an integration test Thymeleaf throws an exception stating that
(org.thymeleaf.extras.springsecurity4.auth.AuthUtils.class)
@SuppressWarnings("unchecked")
private static SecurityExpressionHandler<FilterInvocation> getExpressionHandler(final ServletContext servletContext) {
final ApplicationContext ctx =
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
final Map<String, SecurityExpressionHandler> expressionHandlers =
ctx.getBeansOfType(SecurityExpressionHandler.class);
for (SecurityExpressionHandler handler : expressionHandlers.values()) {
if (FilterInvocation.class.equals(GenericTypeResolver.resolveTypeArgument(handler.getClass(), SecurityExpressionHandler.class))) {
return handler;
}
}
throw new TemplateProcessingException(
"No visible SecurityExpressionHandler instance could be found in the application " +
"context. There must be at least one in order to support expressions in Spring Security " +
"authorization queries.");
This exception is valid because SecurityExpressionHandler.class is missing from the application context during the integration test.
So my question is... how come a SecurityExpressionHandler.class is registered as spring bean in a regular servlet environment but when using the Integration Test config ctx.getBeansOfType(SecurityExpressionHandler.class) is missing from the context? Is this a bug in Spring Security? Or do I need to add additional logic to register a SecurityExpressionHandler bean for the integration test only?
I tried to "force create" a SecurityExpressionHandler by extending GlobalMethodSecurityConfiguration and @Overriding the createExpressionHandler() and adding it to my test config but still the bean was not registered with the WebApplicationContext.
This is a blocker for me right now because I cannot perform any integration testing on any view file that contains Spring Security expressions embedded inside them.
Spring v4.1.6
Spring Security 4.0.1
Thymeleaf v2.1.4