Spring Security – Cache Control Headers – Spring Security – Cache Control Headers

最后修改: 2017年 2月 7日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this article, we’ll explore how we can control HTTP caching with Spring Security.

在这篇文章中,我们将探讨如何用Spring Security控制HTTP缓存。

We’ll demonstrate its default behavior, and also explain the reasoning behind it. We’ll then look at ways to change this behavior, either partially or completely.

我们将演示其默认行为,并解释其背后的原因。然后,我们将研究如何部分或完全改变这种行为。

2. Default Caching Behaviour

2.默认的缓存行为

By using cache control headers effectively, we can instruct our browser to cache resources and avoid network hops. This decreases latency, and also the load on our server.

通过有效地使用缓存控制头,我们可以指示我们的浏览器缓存资源并避免网络跳转。这就减少了延迟,也减少了我们服务器上的负载。

By default, Spring Security sets specific cache control header values for us, without us having to configure anything.

默认情况下,Spring Security为我们设置了特定的缓存控制头值,我们无需配置任何东西。

First, let’s setup Spring Security for our application:

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

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class SpringSecurityConfig {

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

We’re overriding configure() to do nothing, this means that we won’t need to be authenticated to hit an endpoint, enabling us to focus on purely testing caching.

我们重写configure(),不做任何事情,这意味着我们不需要经过认证就可以打到一个端点,使我们能够专注于纯粹的测试缓存。

Next, let’s implement a simple REST endpoint:

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

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

The resulting cache-control header will look like this:

由此产生的cache-control头将看起来像这样。

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

Finally, let’s implement a test which hits the endpoint, and assert what headers are sent in the response:

最后,让我们实现一个测试,点击端点,并断定响应中发送了哪些头信息。

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

Essentially, what this means is that a browser will never cache this response.

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

Whilst this may seem inefficient, there is actually a good reason for this default behavior – If one user logs out and another one logs in, we don’t want them to be able to see the previous users resources. It’s much safer to not cache anything by default, and leave us to be responsible for enabling caching explicitly.

虽然这看起来效率不高,但实际上这种默认行为有一个很好的理由–如果一个用户退出,另一个用户登录,我们不希望他们能够看到前一个用户的资源。默认情况下不缓存任何东西,让我们负责明确地启用缓存,这要安全得多。

3. Overriding the Default Caching Behaviour

3.重写默认缓存行为

Sometimes we might be dealing with resources which we do want to be cached. If we are going to enable it, it would be safest to do on a per resource basis. This means any other resources will still not be cached by default.

有时我们可能会处理一些我们确实希望被缓存的资源。如果我们要启用它,最安全的做法是以每个资源为基础。这意味着任何其他资源在默认情况下仍然不会被缓存。

To do this, let’s try overriding the cache control headers in a single handler method, by use of the CacheControl cache. The CacheControl class is a fluent builder, which makes it easy for us to create different types of caching:

要做到这一点,让我们尝试在一个处理方法中重写缓存控制头,通过使用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));
}

Let’s hit this endpoint in our test, and assert that we have changed the headers:

让我们在我们的测试中击中这个端点,并断言我们已经改变了头文件。

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

As we can see, we’ve overridden the defaults, and now our response will be cached by a browser for 60 seconds.

我们可以看到,我们已经覆盖了默认值,现在我们的响应将被浏览器缓存60秒。

4. Turning Off the Default Caching Behavior

4.关掉默认的缓存行为

We can also turn off the default cache control headers of Spring Security altogether. This is quite a risky thing to do, and not really recommended. But, we if we really want to, then we can try it by creating a SecurityFilterChain bean:

我们也可以完全关闭Spring Security的默认缓存控制头。这是很冒险的事情,并不推荐这样做。但是,如果我们真的想这么做,那么我们可以通过创建一个SecurityFilterChain Bean来尝试。

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

Now, let’s make a request to our endpoint again and see what response we get:

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

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

As we can see, no cache headers have been set at all. Again, this is not secure but proves how we can turn off the default headers if we want to.

正如我们所看到的,根本没有设置任何缓存头。同样,这并不安全,但是证明了如果我们想的话,我们可以关闭默认的头信息。

5. Conclusion

5.总结

This article demonstrates how Spring Security disables HTTP caching by default and explains that this is because we do not want to cache secure resources. We’ve also seen how we can disable or modify this behavior as we see fit.

本文演示了Spring Security如何默认禁用HTTP缓存,并解释了这是因为我们不希望缓存安全资源。我们也看到了我们如何在我们认为合适的时候禁用或修改这种行为。

The implementation of all these examples and code snippets can be found in the GitHub project.

所有这些例子和代码片断的实现都可以在GitHub项目中找到。