Spring Security - 缓存控制头

2023/05/17

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

我们只是简单的返回一个SecurityFilterChain bean,这意味着我们不需要进行身份验证即可访问端点,从而使我们能够专注于纯粹的测试缓存。

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

@Controller
public class ResourceEndpoint {

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

public class UserDto {
    public final String name;

    public UserDto(String name) {
        this.name = name;
    }
}

生成的cache-control标头将如下所示:

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

最后,让我们实现一个访问此端点的测试,并断言响应中发送了哪些标头:

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AppRunner.class)
class ResourceEndpointIntegrationTest {

    @LocalServerPort
    private int serverPort;

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

    private String getBaseUrl() {
        return String.format("http://localhost:%d", serverPort);
    }
}

本质上,这意味着浏览器永远不会缓存此响应。

虽然这看起来效率低下,但这种默认行为实际上有一个很好的原因-如果一个用户注销而另一个用户登录,我们不希望他们能够看到以前的用户资源。默认情况下不缓存任何内容要安全得多,并且将缓存的启用交由我们负责。

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

让我们在测试中访问这个端点,并断言我们已经更改了缓存控制头:

@Test
void whenGetRequestForUser_shouldRespondMaxAgeCacheControl() {
    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();
}

现在,让我们再次向端点发出请求,看看我们得到什么响应:

@Test
void whenGetRequestForUser_shouldRespondWithDefaultCacheHeaders() {
    given().when()
        .get(getBaseUrl() + "/default/users/Michael")
        .then()
        .headers(new HashMap<String, Object>());
}

正如我们所见,根本没有设置缓存标头。

5. 总结

本文演示了Spring Security默认情况下如何禁用HTTP缓存。我们还了解了如何在我们认为合适的情况下禁用或修改此行为。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章