Closed spring-projects-issues closed 8 years ago
Rob Winch said:
You need to ensure a bean by the name of "springSecurityFilterChain" is available if you do not explicitly provide a FilterChainProxy. In the example that was provided, a standalone setup is being used which means MockMvc will not be aware of the WebApplicationContext (and thus not be able to look up the "springSecurityFilterChain" bean).
The easiest way to resolve this is to use something like this:
MockMvc mockMvc = MockMvcBuilders
// replace standaloneSetup with line below
.webAppContextSetup(wac)
.alwaysDo(print())
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
If you really want to use a standaloneSetup (doesn't really make sense since you already have a WebApplicationContext), you can explicitly provide the springSecurityFilterChain using:
@Autowired
FilterChainProxy springSecurityFilterChain;
@Before
public void startMocks(){
controller = wac.getBean(RecipesController.class);
MockMvc mockMvc = MockMvcBuilders
.standaloneSetup(controller)
.alwaysDo(print())
.apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain))
.build();
MockitoAnnotations.initMocks(this);
}
Rob Winch said:
I'm resolving this as works as designed per my previous comment.
If it is not an integration test, looks like the springSecurityFilterChain
is not defined.
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = Application.class)
@ContextConfiguration(classes = MockServletContext.class)
public class LockControllerTest {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(SecurityMockMvcConfigurers.springSecurity())
.alwaysDo(print())
.build();
// mockMvc = MockMvcBuilders.standaloneSetup(lockController).apply(springSecurity()).build();
}
@ezraroi Were you able to overcome this odd behavior somehow? I'm facing same issue with my unit test.
@lukkuz Did you try my suggestion in https://github.com/spring-projects/spring-security/issues/3383#issuecomment-180607733
@ezraroi If the configuration does not load the Spring Security configuration, then there is no security to apply. There must be a bean by the name of springSecurityFilterChain
.
If you want a unit test, then there is no need to use the security features of MockMvc
because Spring Security will not be invoked.
@rwinch I am currently unit testing a controller which has a method like this:
public String doSomething(@AuthenticationPrincipal Authentication authentication) {
...
}
I really just like to have a mocked user in that method in a standalone setup, no need for 'real' security.
mockMvc.perform(MockMvcRequestBuilders.post("/doSomething")
.with(authentication(authentication))
.andExpect(status().isOk());
This fails, the authentication is not available within the method handling the request in the controller.
However, I do not know where to get the springSecurityFilterChain
to use, as I have no context. I tried to mock it, but apparently it filters out every request then:
@Mock
private FilterChainProxy springSecurityFilterChain;
@BeforeTest
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(controller)
.apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain))
.setViewResolvers(viewResolver())
.build();
}
@kappmeier The post processors are really only intended to work with Spring Security's FilterChainProxy
added to the MockMvc
instance. If you need to run the test without Spring Security's FilterChainProxy
, then you can just set the SecurityContextHolder
. For example:
@WithMockUser
@Test
public void doSomething() {
mockMvc.perform(post("/doSomething")
.andExpect(status().isOk());
}
or
@Test
public void doSomething() {
SecurityContextHolder.getContext().setAuthentication(authentication);
mockMvc.perform(post("/doSomething")
.andExpect(status().isOk());
}
I'm trying the second example, but the controller method's parameter is being initialized with a newly created Principal (User) instead of the one I set in SecurityContext. I can see that AuthenticationPrincipalArgumentResolver is not added to the list of argument resolvers. How can I make it appear in that list?
Fixed by adding AuthenticationPrincipalArgumentResolver to the list of resolvers:
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver()).build()
Rémi Doolaeghe (Migrated from SEC-3174) said:
I try to write a unit test for a controller in Spring MVC, involving Spring Security. For isolation purpose, I need to mock the service present in a Rest controller.
To do this, I use this piece of code:
This leads to this result:
java.lang.IllegalStateException: springSecurityFilterChain cannot be null. Ensure a Bean with the name springSecurityFilterChain implementing Filter is present or inject the Filter to be used. at org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurer.beforeMockMvcCreated(SecurityMockMvcConfigurer.java:62) at org.springframework.test.web.servlet.setup.AbstractMockMvcBuilder.build(AbstractMockMvcBuilder.java:133) at fr.easycompany.easycook.springboot.controllers.RecipesControllerCan.startMocks(RecipesControllerCan.java:51)
For more details, I joined the project simplified as much as possible to reproduce the issue.