返回介绍

4.2.2 测试 Web 安全

发布于 2025-04-21 21:10:08 字数 4038 浏览 0 评论 0 收藏

Spring Security 能让你非常方便地测试安全加固后的 Web 应用程序。为了利用这点优势,你必须在项目里添加 Spring Security 的测试模块。要在 Gradle 里做到这一点,你需要的就是以下 testCompile 依赖:

testCompile("org.springframework.security:spring-security-test")

如果你用的是 Maven,则添加以下 <dependency>

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

应用程序的 Classpath 里有了 Spring Security 的测试模块之后,只需在创建 MockMvc 实例时运用 Spring Security 的配置器。

@Before
public void setupMockMvc() {
mockMvc = MockMvcBuilders
    .webAppContextSetup(webContext)
    .apply(springSecurity())
    .build();
}

springSecurity() 方法返回了一个 Mock MVC 配置器,为 Mock MVC 开启了 Spring Security 支持。只需像上面这样运用就行了,Spring Security 会介入 MockMvc 上执行的每个请求。具体的安全配置取决于你如何配置 Spring Security(或者 Spring Boot 如何自动配置 Spring Security)。在阅读列表这个应用程序里,我们在第 3 章里创建 SecurityConfig.java 时,配置也是如此。

springSecurity() 方法 springSecurity()SecurityMockMvcConfigurers 的一个静态方法,考虑到可读性,我已经将其静态导入。

开启了 Spring Security 之后,在请求主页的时候,我们便不能只期待 HTTP 200 响应。如果请求未经身份验证,我们应该期待重定向到登录页面:

@Test
public void homePage_unauthenticatedUser() throws Exception {
mockMvc.perform(get("/"))
    .andExpect(status().is3xxRedirection())
    .andExpect(header().string("Location",
                               "http://localhost/login"));
}

不过,经过身份验证的请求又该如何发起呢?Spring Security 提供了两个注解。

  • @WithMockUser :加载安全上下文,其中包含一个 UserDetails ,使用了给定的用户名、密码和授权。

  • @WithUserDetails :根据给定的用户名查找 UserDetails 对象,加载安全上下文。

在这两种情况下,Spring Security 的安全上下文都会加载一个 UserDetails 对象,添加了该注解的测试方法在运行过程中都会使用该对象。 @WithMockUser 注解是两者里比较基础的那个,允许显式声明一个 UserDetails ,并加载到安全上下文。

@Test
@WithMockUser(username="craig",
              password="password",
              roles="READER")
public void homePage_authenticatedUser() throws Exception {
  ...
}

如你所见, @WithMockUser 绕过了对 UserDetails 对象的正常查询,用给定的值创建了一个 UserDetails 对象取而代之。在简单的测试里,这就够用了。但我们的测试需要 Reader (实现了 UserDetails )而非 @WithMockUser 创建的通用 UserDetails 。为此,我们需要 @WithUserDetails

@WithUserDetails 注解使用事先配置好的 UserDetailsService 来加载 UserDetails 对象。回想一下第 3 章,我们配置了一个 UserDetailsService Bean,它会根据给定的用户名查找并返回一个 Reader 对象。太完美了!所以我们要为测试方法添加 @WithUserDetails 注解,如代码清单 4-4 所示。

代码清单 4-4 测试带有用户身份验证的安全加固方法

@Test
@WithUserDetails("craig")              ←---使用 craig 用户
public void homePage_authenticatedUser() throws Exception {

  Reader expectedReader = new Reader();       ←---配置期望的 Reader
  expectedReader.setUsername("craig");
  expectedReader.setPassword("password");
  expectedReader.setFullname("Craig Walls");

  mockMvc.perform(get("/"))         ←---发起 GET 请求
      .andExpect(status().isOk())
      .andExpect(view().name("readingList"))
      .andExpect(model().attribute("reader",
                         samePropertyValuesAs(expectedReader)))
      .andExpect(model().attribute("books", hasSize(0)))

}

在代码清单 4-4 里,我们通过 @WithUserDetails 注解声明要在测试方法执行过程中向安全上下文里加载 craig 用户。 Reader 会放入模型,该测试方法先创建了一个期望的 Reader 对象,后续可以用来进行比较。随后 GET 请求发起,也有了针对视图名和模型内容的断言,其中包括名为 reader 的模型属性。

同样,此处没有启动 Servlet 容器来运行这些测试,Spring 的 Mock MVC 取代了实际的 Servlet 容器。这样做的好处是测试方法运行相对较快。因为不需要等待服务器启动,而且不需要打开 Web 浏览器发送表单,所以测试比较简单快捷。

不过,这并不是一个完整的测试。它比直接调用控制器方法要好,但它并没有真的在 Web 浏览器里执行应用程序,验证呈现出的视图。为此,我们需要启动一个真正的 Web 服务器,用真实浏览器来访问它。让我们来看看 Spring Boot 如何启动一个真实的 Web 服务器来帮助测试。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。