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
If you are loading the
WebApplicationContext
for your test using@ContextHierarchy
, then this will not work with Spring Framework 4.1.4 through 4.1.6 due to a confirmed bug that will be fixed in 4.1.7.See SPR-13075 for details.