13

I'm struggling with testing access control on URLs protected by Spring Security.

The configuration looks like this:

    http
            .authorizeRequests()
            .antMatchers("/api/user/**", "/user").authenticated()
            .antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasAuthority("ADMIN")
            .anyRequest().permitAll();

And the test class looks like this:

package com.kubukoz.myapp;

import com.kubukoz.myapp.config.WebSecurityConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import javax.transaction.Transactional;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {MyApplication.class, WebSecurityConfig.class})
@WebAppConfiguration
@TransactionConfiguration(defaultRollback = true)
@Transactional(rollbackOn = Exception.class)
public class MyApplicationTests {

    @Autowired
    private WebApplicationContext context;
    private MockMvc mockMvc;
    @Autowired
    private FilterChainProxy filterChainProxy;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .dispatchOptions(true)
                .addFilters(filterChainProxy)
                .build();
    }

    @Test
    public void testAnonymous() throws Exception {
        mockMvc.perform(get("/api/user/account")).andExpect(status().is3xxRedirection());
    }

    @Test
    public void testUserAccessForAccount() throws Exception{
        mockMvc.perform(get("/api/user/account")).andExpect(status().isOk());
    }
}

What's the easiest way to make the last two tests pass? @WithMockUser didn't work.

3
  • Tried by annotating your methods with @PreAuthorize("hasPermission('ADMIN')") Commented Aug 4, 2015 at 14:39
  • 1
    Sounds like repeating code, as I already authorize them in the security config, and it does work - with actual users, not in tests Commented Aug 4, 2015 at 14:52
  • I can't autowire filter chain proxy, what am i doing wrong? Commented Mar 12, 2018 at 22:46

2 Answers 2

17

You should not add the FilterChainProxy directly. Instead, you should apply SecurityMockMvcConfigurers.springSecurity() as indicated by the reference. An example is included below:

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

The result of this is:

  • the FilterChainProxy is added as a Filter to MockMvc (as you did)
  • the TestSecurityContextHolderPostProcessor is added

Why is TestSecurityContextHolderPostProcessor necessary? The reason is that we need to communicate the current user from the test method to the MockHttpServletRequest that is created. This is necessary because Spring Security's SecurityContextRepositoryFilter will override any value on SecurityContextHolder to be the value found by the current SecurityContextRepository (i.e. the SecurityContext in HttpSession).

Update

Remember anything that contains role in the method name automatically prefixes "ROLE_" to the string that was passed in.

Based on your comment, the problem is you need to either update your configuration to use hasRole instead of hasAuthority (since your annotation is using roles):

.authorizeRequests()
            .antMatchers("/api/user/**", "/user").authenticated()
            .antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasRole("ADMIN")
            .anyRequest().permitAll();

Alternatively

You in Spring Security 4.0.2+ you can use:

@WithMockUser(authorities="ADMIN")
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks. Now I can use with(user("...").roles("ADMIN")) - but a @WithMockUser(roles="ADMIN") call to (admin role protected) /admin/health still gives me a 403, as if the roles parameter was ignored :/
Can you provide a link to a new question or update your configuration?
Actually, I figured it out. See the udpate
I am indeed using authorities in my config, but changing roles to authorities here didn't help - getting a 302 to /login this time. I have a gradle dependency for spring-security-web:4.0.2.RELEASE.
Please post new question with all the details. You can post link in comments and I'll respond
|
1

Okay, figured it out.

mockMvc.perform(get("/api/user/account")
      .with(user("user")))
      .andExpect(status().isOk());

It works now.

5 Comments

Spring Security allows to specify the actual user of a request in multiple ways. You should also be able to annotate the test method with something like @WithMockUser(roles="ADMIN") as explained in the referenced documentation
The thing is - @WithMockUser(roles = "USER") didn't work with the method, contrary to with(user(...)). Also, mockMvc.perform(get("/admin/health").with(user("user").roles("ADMIN"))).andExpect(status().isOk()); doesn't work either. I totally don't get it
Where is the static user() defined (Eclipse fails totally on including static)
Hey, it's org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user, most likely from the spring-security-test library.
.with(user("user")) ignores any kind of permission or role based security

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.