The problem with the (my) accepted answer is:
(just for the show case, but:) We modify "singleton scope property" on (every) request!!!
When we add a "stress" test wrapper like this.
( ... wait until all threads finish their work in java ?? -> ExecutorCompletionService, since Java:1.5;)
It badly fails (header has not the "expected" value):
@Test
void testParallel() throws Exception {
// 200 cycles, with 0 (== #cpu) threads ...
final StressTester<Void> stressTestHome = new StressTester<>(Void.class, 200, 0, // ... and these (three) jobs (firing requests at our app):
() -> {
home(); // here the original tests
return null;
},
() -> {
foo(); // ... with assertions ...
return null;
},
() -> {
bar(); // ... moved to private (non Test) methods
return null;
}
);
stressTestHome.test(); // run it, collect it and:
stressTestHome.printErrors(System.out);
assertTrue(stressTestHome.getExceptionList().isEmpty());
}
As in mock as in (full) server mode... ;(;(;(
We will encounter the same problem, when we want to change that header from a "lower scope" (than singleton..so any other scope:) ;(;(;(
If we want singleton scope policy for that header, and only "trigger the reload" (for all subsequent requests), we can stop reading. (answer 1 is ok, as i actually "initially understood" the question & answered:)
But if we want that "per request header" with spring-security, we have to pass this test! :)
One possible solution: Method Injection!
So back to our custom HeaderWriter
implementation:
package com.example.demo;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
// abstract!
public abstract class MyContentSecurityPolicyHeaderWriter implements HeaderWriter {
// ... no state!!!
public static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy";
public static final String DEFAULT_SRC_SELF_POLICY = "default-src 'self'";
@Override // how cool, that there is a HttpServletRequest/-Response "at hand" !?!
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
if (!response.containsHeader(CONTENT_SECURITY_POLICY_HEADER)) {
// responsible for the header key, but for the value we ask: delegate
response.setHeader(CONTENT_SECURITY_POLICY_HEADER, policyDelegate().getPolicyDirectives());
}
}
// TLDR xDxD
protected abstract MyContentSecurityDelegate policyDelegate();
}
Thanks, again!;)
With this tiny (but managed) "context holder":
package com.example.demo;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class MyContentSecurityDelegate {
@Getter
@Setter
private String policyDirectives;
}
We do this (with spring-java-config, How to create bean using @Bean in spring boot for abstract class):
@Configuration
class FreakyConfig {
@Value("${my.policy.directive:DEFAULT_SRC_SELF_POLICY}")
private String policy;
@Bean
@RequestScope // !! (that is suited for our controllers)
public MyContentSecurityDelegate delegate() {
return MyContentSecurityDelegate.of(policy);
}
@Bean
public MyContentSecurityPolicyHeaderWriter myWriter() {
return new MyContentSecurityPolicyHeaderWriter() { // anonymous inner class
@Override
protected MyContentSecurityDelegate policyDelegate() {
return delegate(); // with request scoped delegate.
}
};
}
}
..then our controllers do that (autowire & "talk" to the delegate):
@Autowired // !
private MyContentSecurityDelegate myRequestScopedDelegate;
@GetMapping("/foo")
public String foo() {
// !!
myRequestScopedDelegate.setPolicyDirectives("FOO");
return "Hello from foo!";
}
Then all tests pass! :) pushed to (same)github.
But to achieve the goal: "Write headers request (even thread) specific", we can use any other technique (matching our stack & needs, beyond spring-security):
Mo' Links:
Happy Coding!