如何将 Spring SecurityContext 传递给 RestAssured

use*_*011 4 java testing spring-security rest-assured spring-boot

我正在尝试为具有方法级安全控制器的 Spring-Boot 应用程序设置 RestAssured 测试。

例如,我有一个使用方法级安全性的最小控制器


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

和宽松的安全配置


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

然后使用 RestAssured 的这个简单测试失败了:

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

即使模拟用户配置了正确的角色,为什么此测试仍会失败?

我已经对此进行了调试,似乎运行测试的线程中的 SecurityContext 设置正确,而处理 RestAssured 请求的线程中的 SecurityContext 未填充。但为什么?

use*_*011 5

所以我终于明白出了什么问题。这是我发现的:

注入 SecurityContext 仅在单元测试中有意义,但原始测试试图成为集成测试。

有两种出路:

  1. 使测试成为适当的单元测试。那么你应该使用RestAssuredMockMvc.given()而不是RestAssured.given(). 例如,

    @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();
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    会起作用,但那时它只是一个单元测试。

  2. 使测试成为适当的集成测试。这将涉及构建正确的身份验证,并配置测试请求的主体,以便根据生产代码的需要填充 SecurityContext 。与 RestAssured 一起走这条路看起来会像这样:

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