简体   繁体   中英

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?

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()); } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM