1. 概述

本篇文章将探讨如何通过Spring Security控制HTTP缓存。我们将展示其默认行为,并解释其背后的逻辑,然后讨论如何部分或完全改变这种行为。

2. 默认缓存行为

有效地使用缓存控制头,我们可以指导浏览器缓存资源,避免网络跳转,从而减少延迟并减轻服务器负载。默认情况下,Spring Security会为我们设置特定的缓存控制头,无需我们进行任何配置。

首先,让我们为应用程序设置Spring Security:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class SpringSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http.build();
    }
}

我们重写了configure()方法,使其无操作,这样就不需要认证即可访问端点,以便专注于纯粹的缓存测试。

接下来,实现一个简单的REST端点:

@GetMapping("/default/users/{name}")
public ResponseEntity<UserDto> getUserWithDefaultCaching(@PathVariable String name) {
    return ResponseEntity.ok(new UserDto(name));
}

响应中的cache-control头看起来像这样:

[cache-control: no-cache, no-store, max-age=0, must-revalidate]

最后,实现一个测试,调用端点并断言响应中发送的头信息:

given()
  .when()
  .get(getBaseUrl() + "/default/users/Michael")
  .then()
  .header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
  .header("Pragma", "no-cache");

基本上这意味着浏览器永远不会缓存这个响应。虽然这可能看起来效率不高,但默认行为背后有一个好理由:如果一个用户登出,另一个用户登录,我们不希望他们能看到前一个用户的资源。 默认不缓存任何内容更为安全,让我们负责明确启用缓存。

3. 覆盖默认缓存行为

有时,我们可能处理的是确实希望被缓存的资源。如果要启用它,最好按资源进行。这意味着其他资源仍然默认不会被缓存。

要做到这一点,我们可以尝试在单个处理器方法中使用CacheControl缓存头来覆盖默认值。CacheControl类是一个链式构建器,使我们能够轻松创建不同类型的缓存:

@GetMapping("/users/{name}")
public ResponseEntity<UserDto> getUser(@PathVariable String name) { 
    return ResponseEntity.ok()
      .cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS))
      .body(new UserDto(name));
}

在测试中请求这个端点,并断言我们已更改了头信息:

given()
  .when()
  .get(getBaseUrl() + "/users/Michael")
  .then()
  .header("Cache-Control", "max-age=60");

如我们所见,我们已覆盖默认值,现在我们的响应将在浏览器中缓存60秒。

4. 关闭默认缓存行为

我们还可以完全关闭Spring Security的默认缓存控制头。这是一项相当冒险的操作,不建议这样做。但如果我们真的想这么做,可以通过创建SecurityFilterChain bean来尝试:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.headers().disable();
    return http.build();
}

现在,再次请求我们的端点,看看我们得到什么响应:

given()
  .when()
  .get(getBaseUrl() + "/default/users/Michael")
  .then()
  .headers(new HashMap<String, Object>());

如我们所见,根本没有设置任何缓存头。再次强调,这不安全,但证明了如果我们愿意,可以如何关闭默认头信息。

5. 总结

本文展示了Spring Security如何默认禁用HTTP缓存,并解释了这是因为我们不想缓存安全资源。我们也了解了如何根据需要调整或关闭这种行为。

所有示例和代码片段的实现可以在GitHub项目中找到。