Spring 没有特定角色的用户的安全测试端点

Spring Security testing Endpoint for user without specific role

我正在为我的 Spring MVC 控制器编写一些集成测试。

控制器受 Spring 安全保护。

这是测试class我目前有:

@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.MOCK,
    classes = GuiBackendApplication.class
)
@AutoConfigureMockMvc
public class ConfigEditorControllerIntegrationTest {

    @Autowired
    private MockMvc mockedMvc;

    @Test
    @WithMockUser(username = "user", password = "password", roles = {"admin"})
    public void adminsCanAccessRuntimeConfig() throws Exception {

        this.mockedMvc.perform(get("/my/custom/api"))
            .andExpect(status().isOk());
    }
}

此测试 class 确保管理员可以访问我的端点。它工作正常。

但是如果我想测试是否只有具有管理员角色的用户才能访问我的端点怎么办?

我可以编写一个测试,使用 @WithMockUsers 和我目前拥有的所有角色,除了管理员角色。但这对我来说很难维护。我希望我的测试确保只有具有管理员角色的用户才能访问我的端点,而不管任何新角色。

我检查了 Spring 参考文档,但没有找到任何相关信息。有办法实现吗?

像这样

@Test
@WithMockUser(username = "user", password = "password", roles = {"IS NOT admin"})
public void nonAdminsCannotAccessRuntimeConfig() throws Exception {

    this.mockedMvc.perform(get("/my/custom/api"))
        .andExpect(status().isUnauthorized());
}

Spring安全不知道你的系统定义了什么角色。所以如果你想对所有可用角色都有 100% 的测试覆盖率,你必须一一告诉它并测试它。

您可以通过使用 JUnit 5 的 @ParameterizedTest 并使用具有不同角色的 UserRequestPostProcessor 配置 MockMvc 以维护方式轻松完成此操作。

类似于:

public class ConfigEditorControllerIntegrationTest {

    @ParameterizedTest
    @MethodSource
    public void nonAdminsCannotAccessRuntimeConfig(String role) throws Exception {
        mockedMvc.perform(get("/my/custom/api")
                 .with(user("someUser").roles(role)))
                 .andExpect(status().isUnauthorized());
    }

    static List<String> nonAdminsCannotAccessRuntimeConfig() {
        return Roles.exclude("admin");
    }
}

并创建一个 class 来维护所有可用的角色 :

public class Roles {

    public static List<String> all() {
        return List.of("admin", "hr", "developer" , "accountant" , .... , "devops");
    }

    public static List<String> exclude(String excludeRole) {
        List<String> result = new ArrayList<>(all());
        result.remove(excludeRole);
        return result;
    }

}