问题
How can i enable samesite for my web application which runs on wildfly as. Checked standalone.xml however could not find an appropriate tag within
<servlet-container name="default">
<session-cookie http-only="true" secure="true"/>
<jsp-config/>
</servlet-container>
回答1:
As for now the Java Servlet 4.0 specification doesn't support the SameSite cookie attribute. You can see available attributes by opening javax.servlet.http.Cookie java class.
However, there are a couple of workarounds. You can override Set-Cookie attribute manually.
The first approach (using Spring's AuthenticationSuccessHandler):
import java.io.IOException;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
addSameSiteCookieAttribute(response); // add SameSite=strict to Set-Cookie attribute
response.sendRedirect("/hello"); // redirect to hello.html after success auth
}
private void addSameSiteCookieAttribute(HttpServletResponse response) {
Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
boolean firstHeader = true;
for (String header : headers) { // there can be multiple Set-Cookie attributes
if (firstHeader) {
response.setHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=Strict"));
firstHeader = false;
continue;
}
response.addHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=Strict"));
}
}
}
The second approach (using javax.servlet.Filter):
import java.io.IOException;
import java.util.Collection;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
public class SameSiteFilter implements javax.servlet.Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
addSameSiteCookieAttribute((HttpServletResponse) response); // add SameSite=strict cookie attribute
}
private void addSameSiteCookieAttribute(HttpServletResponse response) {
Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
boolean firstHeader = true;
for (String header : headers) { // there can be multiple Set-Cookie attributes
if (firstHeader) {
response.setHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=Strict"));
firstHeader = false;
continue;
}
response.addHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=Strict"));
}
}
@Override
public void destroy() {
}
}
You can look at this demo project on the GitHub for more details on the configuration for org.springframework.security.web.authentication.AuthenticationSuccessHandler or javax.servlet.Filter.
The WebSecurityConfig contains all the necessary configuration.
Using addHeader is not guaranteed to work because basically the Servlet container manages the creation of the Session and Cookie. For example, both approaches won't work in case you return JSON in response body because application server will overwrite Set-Cookie header during flushing of response. However, above approaches will work in cases, when you redirect a user to another page after successful authentication.
回答2:
One workaround is to hack the SameSite
setting into the cookie by using another attribute (e.g. comment
):
<servlet-container name="default">
<jsp-config/>
<session-cookie comment="; SameSite=None"/>
<websockets/>
</servlet-container>
But because Undertow quotes the comment (and other) values when using version 0 or version 1 cookies, JBoss/WildFly needs to be running with the io.undertow.cookie.DEFAULT_ENABLE_RFC6265_COOKIE_VALIDATION
system property set to true
:
./bin/standalone.sh -Dio.undertow.cookie.DEFAULT_ENABLE_RFC6265_COOKIE_VALIDATION=true
This will give you the desired result:
This approach is obviously hacky, and relies entirely on Undertow implementation details, so I'd recommend configuring on the web server or load balancer level instead.
回答3:
For Spring Boot with the currently latest release:
If you do not have the latest spring-boot-starter-tomcat check the SameSiteCookies enum for value UNSET
, if the value is missing you need a newer release because it will skip the value SameSite=None
.
@Component
public class SameSiteTomcatCookieProcessorCustomizationBean implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>
{
@Override
public void customize(TomcatServletWebServerFactory server) {
server.getTomcatContextCustomizers().add(new TomcatContextCustomizer()
{
@Override
public void customize(Context context)
{
Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies("None");
context.setCookieProcessor(cookieProcessor);
}
});
}
}
回答4:
My workaround, which works in JBoss EAP 7.2, is a custom handler. I use it as a global handler. But you can also use it in the jboss-web.xml. You need to play with the cookie implementation because undertow only allows Strict or Lax for samesite (it throws '"UT000162: Same-site attribute None is invalid. It must be Strict or Lax"' if you use cookie.setSameSiteMode("None"))
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
import java.lang.reflect.Proxy;
import java.util.Map;
public class CookieSameSiteHandler implements HttpHandler
{
private HttpHandler next;
public CookieSameSiteHandler(HttpHandler next){
this.next = next;
}
@Override
public void handleRequest(final HttpServerExchange exchange)
throws Exception
{
exchange.addResponseCommitListener(serverExchange -> {
for (Map.Entry<String, Cookie> responcecookie : serverExchange.getResponseCookies().entrySet()){
serverExchange.getResponseCookies().replace(responcecookie.getKey(), proxyCookie(responcecookie.getValue()));
}
});
next.handleRequest(exchange);
}
private Cookie proxyCookie(Cookie cookie)
{
return (Cookie)Proxy.newProxyInstance(
cookie.getClass().getClassLoader(),
cookie.getClass().getInterfaces(),
(proxy, method, args) -> {
if ("isSameSite".equals(method.getName())){
return true;
}
if ("getSameSiteMode".equals(method.getName()) && cookie.getSameSiteMode() == null){
return "None";
}
if ("isSecure".equals(method.getName()) && cookie.getSameSiteMode() == null){
return true;
}
return method.invoke(cookie, args);
});
}
}
handler configuration:
<subsystem xmlns="urn:jboss:domain:undertow:7.0" default-virtual-host="default-host">
<buffer-cache name="default"/>
<server name="default-server" default-host="default-host">
...
<host name="default-host" alias="localhost,example.com">
...
<filter-ref name="cookiehandler"/>
...
</host>
</server>
...
<filters>
<filter class-name="nl.myownstuff.handler.CookieSameSiteHandler" module="nl.myownstuff.undertow" name="cookiehandler"/>
</filters>
</subsystem>
来源:https://stackoverflow.com/questions/49697449/how-to-enable-samesite-for-jsessionid-cookie