DGoodman
DGoodman

Reputation: 125

PreAuthorize annotations on Spring controllers are ignored when testing

I'm testing my Spring MVC controllers using JUnit, Mockito & spring-test. Unfortunately, the tests are ignoring the @PreAuthorize annotations on my controller methods, and I've been unable to resolve this. Key code snippets are below, though I've removed irrelevant logic for mocking responses from MyController's dependencies to keep it short. I'm using Spring 3.2 & JUnit 4, and I'm running the tests both via Maven and directly through Eclipse (Run as -> JUnit test).

Right now I am expecting the getAccounts_ReturnsOkStatus test to fail, as I have not provided any auth and the method that the /accounts route maps onto is annotated with a pre-auth annotation, however the method is being invoked and the pre-auth check bypassed.

MyControllerTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public class MyControllerTest {

    private MockMvc mockMvc;

    @Mock
    private MyService myService;

    @InjectMocks
    private MyController myController;

    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);  
        mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
    }

   @Test
   public void getAccounts_ReturnsOkStatus() throws Exception {
       // Make the GET request, and verify that it returns HttpStatus.OK (200)
       mockMvc.perform(MockMvcRequestBuilders.get("/accounts"))
              .andExpect(MockMvcResultMatchers.status().isOk());
   }
}

applicationContext-test.xml

<sec:global-method-security pre-post-annotations="enabled" />
<bean id="applicationContextProvider" class="com.myapp.springsupport.ApplicationContextProvider" />

<sec:authentication-manager alias="authenticationManager">
   <sec:authentication-provider>
       <sec:user-service>
           <sec:user name="test-superuser" password="test-pwd" authorities="ROLE_SUPERUSER" />
       </sec:user-service>
   </sec:authentication-provider>
</sec:authentication-manager>

MyController.java

@PreAuthorize("isAuthenticated() and hasRole('ROLE_SUPERUSER')")
@RequestMapping(value = "/accounts", method = RequestMethod.GET)
@ResponseBody
public Collection<Account> getAccounts() {
    return new ArrayList<Account>();
}

The applicationContext-test app context is definitely being used, since manually authenticating using

Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
SecurityContextHolder.getContext().setAuthentication(am.authenticate(auth));

only works with the user credentials specified in the test config (in the absence of any other authentication-provider). Additionally, I can be sure that the pre-auth is being ignored, since I've tested using SEL to invoke a method & debugged.

What am I missing?

Upvotes: 5

Views: 10662

Answers (2)

user1928596
user1928596

Reputation: 1753

MockMvcBuilders.standaloneSetup gets a MyController instantiated manually ( without Spring and therefore without AOP). Therefore the @PreAuthorize is not intercepted and security check is skipped. You can therefore @Autowire your controller and pass it to MockMvcBuilders.standaloneSetup to Mock MyService use @MockBean so that every instance of the service gets replaced with the Mock.

Upvotes: -1

Ren&#233; Scheibe
Ren&#233; Scheibe

Reputation: 2080

You have to enable spring security for testing when building mockMvc.

In Spring 4 it's like this:

mockMvc = MockMvcBuilders.webAppContextSetup(context)
                         .apply(springSecurity())
                         .build();

In Spring 3 it's like this:

@Autowired
private Filter springSecurityFilterChain;
...
mockMvc = MockMvcBuilders.webAppContextSetup(context)
                         .addFilters(springSecurityFilterChain)
                         .build();

For more details see:

Upvotes: 2

Related Questions