Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass Spring SecurityContext to RestAssured

I'm trying to set up a RestAssured test for a Spring-Boot application with method-level secured controllers.

For example, I have this minimal controller using method level security


@RestController
public class DummyController {
    @GetMapping("/")
    @PreAuthorize("hasRole('TEST')") // removing this should make the test green
    public String test() {
        return "hello";
    }
}

and a permissive security configuration


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().permitAll();
    }
}

Then this simple test using RestAssured fails:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class DummyControllerITest {
    private static final Logger logger = LoggerFactory.getLogger(DummyControllerITest.class);

    @LocalServerPort
    private int port;

    @Test
    @WithMockUser(roles = "TEST")
    public void name() throws Exception {
        RestAssured.given()
                .port(port)
            .when()
                .get("/")
            .then()
                .statusCode(HttpStatus.OK.value());
    }
}

Why does this test fail, even though the mock user is configured with the correct role?

I have debugged this and it seems that the SecurityContext in the thread running the test is set up correctly, while the SecurityContext in the thread processing the RestAssured request is not populated. But... why?

like image 204
user1978011 Avatar asked Oct 11 '25 18:10

user1978011


1 Answers

So I finally figured out what was wrong. Here's what I found out:

Injecting a SecurityContext only makes sense in unit tests, but the original test tries to be an integration test.

There are two ways out:

  1. Make the test a proper unit test. Then you should use RestAssuredMockMvc.given() instead of RestAssured.given(). For example,

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
    @RunWith(SpringRunner.class)
    public class DummyControllerITest {
      @Autowired
      private WebApplicationContext webAppContextSetup;
    
      @Test
      @WithMockUser(roles = "TEST")
      public void name() throws Exception {
          RestAssuredMockMvc.webAppContextSetup(webAppContextSetup);
          RestAssuredMockMvc.given()
              .when()
                  .get("/")
              .then()
                  .statusCode(HttpStatus.OK.value());
          RestAssuredMockMvc.reset();
      }
    }
    

    will work, but it will only be a unit test then.

  2. Make the test a proper integration test. This will involve to build a proper authentication, and configure the principal of the test request such that the SecurityContext will be populated as desired by the production code. Going that route with RestAssured will then look like something along these lines:

    @Test
    @WithMockUser(roles = "TEST")
    public void name() throws Exception {
        given()
                .auth().basic("testuser", "password") // ######
                .port(port)
            .when()
                .get("/")
            .then()
                .statusCode(HttpStatus.OK.value());
    }
    

Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!