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

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


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


和许可的安全配置

@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。但为什么?

最佳答案

因此,我终于弄清楚了什么地方出了问题。这是我发现的:

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

有两种解决方法:


使测试成为适当的单元测试。然后,您应该使用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();
  }
}


可以使用,但那时只能进行单元测试。
使测试成为适当的集成测试。这将涉及建立适当的身份验证,并配置测试请求的主体,以使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());
}

09-10 23:11