Spring Boot:如何在运行时更改内容安全策略?

Ala*_*n47 2 java spring content-security-policy spring-boot

我正在尝试热重新加载 Spring Boot 应用程序的内容安全策略(CSP)中的更改,即用户应该能够通过管理 UI 更改它,而无需重新启动服务器。

Spring Boot 中常规的做法是:

@Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) {
        // ... lots more config here...
        http.headers()
            .addHeaderWriter(
                 StaticHeadersWriter(
                     "Content-Security-Policy", 
                     "<some policy string>"
                 )
            )
    } 
}
Run Code Online (Sandbox Code Playgroud)

...但这不允许在分配后重新配置。

我可以在运行时(重新)配置它吗?重新加载应用程序上下文不是一个选项,我只需要能够适应这个特定的设置。

xer*_*593 5

Easy-Peasy,我们只需要公开一个(n适当的)HeaderWriter作为bean!ContentSecurityPolicyHeaderWriter看起来对我们来说合适且足够,但我们也可以自由地实现自定义:

private static final String DEFAULT_SRC_SELF_POLICY = "default-src 'self'";

@Bean
public ContentSecurityPolicyHeaderWriter myWriter(
        @Value("${#my.policy.directive:DEFAULT_SRC_SELF_POLICY}") String initalDirectives
) {
  return new ContentSecurityPolicyHeaderWriter(initalDirectives);
}
Run Code Online (Sandbox Code Playgroud)

然后用:

@Autowired
private ContentSecurityPolicyHeaderWriter myHeadersWriter;

@Override
public void configure(HttpSecurity http) throws Exception {
  // ... lots more config here...
  http.headers()
    .addHeaderWriter(myHeadersWriter);
}
Run Code Online (Sandbox Code Playgroud)

...,我们可以使用这些演示控制器更改标头值:

@GetMapping("/")
public String home() {
  myHeadersWriter.setPolicyDirectives(DEFAULT_SRC_SELF_POLICY);
  return "header reset!";
}

@GetMapping("/foo")
public String foo() {
  myHeadersWriter.setPolicyDirectives("FOO");
  return "Hello from foo!";
}

@GetMapping("/bar")
public String bar() {
  myHeadersWriter.setPolicyDirectives("BAR");
  return "Hello from bar!";
}
Run Code Online (Sandbox Code Playgroud)

我们可以测试:

@SpringBootTest
@AutoConfigureMockMvc
class DemoApplicationTests {

  @Autowired
  private MockMvc mockMvc;

  @Test
  public void testHome() throws Exception {
    this.mockMvc.perform(get("/"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("header reset!")))
            .andExpect(header().string(CONTENT_SECURITY_POLICY_HEADER, DEFAULT_SRC_SELF_POLICY));
  }

  @Test
  public void testFoo() throws Exception {
    this.mockMvc.perform(get("/foo"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("Hello from foo!")))
            .andExpect(header().string(CONTENT_SECURITY_POLICY_HEADER, "FOO"));
  }

  @Test
  public void testBar() throws Exception {
    this.mockMvc.perform(get("/bar"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("Hello from bar!")))
            .andExpect(header().string(CONTENT_SECURITY_POLICY_HEADER, "BAR"));
  }
}
Run Code Online (Sandbox Code Playgroud)

...也在浏览器中:

更改标题浏览器截图

全部集中在一个 github 上。(对不起,主课上的所有人!:)


参考资料:仅限此

  • 感谢您提供这个非常全面的答案!实现 HeaderWriter 看起来是正确的地方,我要尝试一下:) (2认同)