Nik*_*res 16 cookies spring-boot samesite
是否可以在 Spring Boot 中设置Same-Site Cookie标志?
我在 Chrome 中的问题:
与http://google.com/上的跨站点资源关联的 cookie 设置为没有该
SameSite属性。未来版本的 Chrome 将仅在跨站点请求中使用SameSite=None和设置 cookie 时才传送 cookieSecure。您可以在应用程序>存储>Cookies 下的开发人员工具中查看 cookie,并在https://www.chromestatus.com/feature/5088147346030592和 https://www.chromestatus.com/feature/5633521622188032 上查看更多详细信息 。
如何解决这个问题呢?
Eug*_*yuk 17
Spring Boot 2.6.0 现在支持 SameSite cookie 属性的配置:
通过属性进行配置
server.servlet.session.cookie.same-site=strict
Run Code Online (Sandbox Code Playgroud)
通过代码配置
server.servlet.session.cookie.same-site=strict
Run Code Online (Sandbox Code Playgroud)
Spring Boot 2.5.0-SNAPSHOT 不支持 SameSite cookie 属性,并且没有启用它的设置。
Java Servlet 4.0 规范不支持 SameSite cookie 属性。您可以通过打开javax.servlet.http.Cookie java 类来查看可用属性。
但是,有一些解决方法。您可以手动覆盖 Set-Cookie 属性。
第一种方法(使用自定义 Spring HttpFirewall)和请求包装器:
您需要在会话创建后立即包装请求并调整 cookie。您可以通过定义以下类来实现它:
一个 bean(如果您想将所有内容保存在一个地方,您可以在 SecurityConfig 中定义它。为了简洁起见,我只是在其上添加了 @Component 注释)
import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {
@Bean
public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
return CookieSameSiteSupplier.ofStrict();
}
}
Run Code Online (Sandbox Code Playgroud)
第一个包装类
package hello.approach1;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.stereotype.Component;
@Component
public class CustomHttpFirewall implements HttpFirewall {
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
return new RequestWrapper(request);
}
@Override
public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
return new ResponseWrapper(response);
}
}
Run Code Online (Sandbox Code Playgroud)
第二个包装类
package hello.approach1;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.http.HttpHeaders;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* Wrapper around HttpServletRequest that overwrites Set-Cookie response header and adds SameSite=None portion.
*/
public class RequestWrapper extends FirewalledRequest {
/**
* Constructs a request object wrapping the given request.
*
* @param request The request to wrap
* @throws IllegalArgumentException if the request is null
*/
public RequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* Must be empty by default in Spring Boot. See FirewalledRequest.
*/
@Override
public void reset() {
}
@Override
public HttpSession getSession(boolean create) {
HttpSession session = super.getSession(create);
if (create) {
ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (ra != null) {
overwriteSetCookie(ra.getResponse());
}
}
return session;
}
@Override
public String changeSessionId() {
String newSessionId = super.changeSessionId();
ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (ra != null) {
overwriteSetCookie(ra.getResponse());
}
return newSessionId;
}
private void overwriteSetCookie(HttpServletResponse response) {
if (response != null) {
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=None")); // set
firstHeader = false;
continue;
}
response.addHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=None")); // add
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
第二种方法(使用Spring的AuthenticationSuccessHandler):
此方法不适用于基本身份验证。在基本身份验证的情况下,响应在控制器返回响应对象之后、调用 SameSiteFilter#addSameSiteCookieAttribute 之前立即刷新/提交。
package hello.approach1;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
/**
* Dummy implementation.
* To be aligned with RequestWrapper.
*/
public class ResponseWrapper extends HttpServletResponseWrapper {
/**
* Constructs a response adaptor wrapping the given response.
*
* @param response The response to be wrapped
* @throws IllegalArgumentException if the response is null
*/
public ResponseWrapper(HttpServletResponse response) {
super(response);
}
}
Run Code Online (Sandbox Code Playgroud)
第三种方法(使用javax.servlet.Filter):
此方法不适用于基本身份验证。在基本身份验证的情况下,响应在控制器返回响应对象之后、调用 SameSiteFilter#addSameSiteCookieAttribute 之前立即刷新/提交。
package hello.approach2;
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"));
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在 GitHub 上查看此演示项目,了解有关 org.springframework.security.web.authentication.AuthenticationSuccessHandler 或 javax.servlet.Filter 配置的更多详细信息。
SecurityConfig包含所有必要的配置。
使用 addHeader 不能保证有效,因为基本上 Servlet 容器管理 Session 和 Cookie 的创建。例如,如果您在响应正文中返回 JSON,则第二种和第三种方法将不起作用,因为应用程序服务器将在刷新响应期间覆盖 Set-Cookie 标头。但是,当您在成功身份验证后将用户重定向到另一个页面时,第二种和第三种方法将起作用。
请注意,Postman 不渲染/支持 Cookies 部分下的 SameSite cookie 属性(至少在撰写本文时)。您可以查看 Set-Cookie 响应标头或使用curl 查看是否添加了 SameSite cookie 属性。
小智 9
从 Spring Boot 版本 2.6.+ 开始,您可以通过编程方式或通过配置文件指定您的 SameSite Cookie。
如果您想通过配置文件将 Samesite 设置为 lax,则:
server.servlet.session.cookie.same-site=lax
Run Code Online (Sandbox Code Playgroud)
或者以编程方式
@Configuration
public class MySameSiteConfiguration {
@Bean
public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
return CookieSameSiteSupplier.ofLax();
}
}
Run Code Online (Sandbox Code Playgroud)
小智 8
这是 Spring Security 的一个未解决的问题(https://github.com/spring-projects/spring-security/issues/7537)
正如我在 Spring-Boot ( 2.1.7.RELEASE) 中检查的那样,默认情况下它使用DefaultCookieSerializer带有sameSite默认为Lax.
您可以通过以下代码在应用程序启动时修改它。
注意:这是一个黑客,直到真正的修复(配置)在下一个春季发布时公开。
@Component
@AllArgsConstructor
public class SameSiteInjector {
private final ApplicationContext applicationContext;
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
DefaultCookieSerializer cookieSerializer = applicationContext.getBean(DefaultCookieSerializer.class);
log.info("Received DefaultCookieSerializer, Overriding SameSite Strict");
cookieSerializer.setSameSite("strict");
}
}
Run Code Online (Sandbox Code Playgroud)
自从上次更新后,chrome 也开始向我显示该消息。这并不是关于 spring 的真正答案,但您可以将 cookie 标志添加到会话的标头中。就我而言,由于我使用的是 Spring Security,因此我打算在用户登录时添加它,因为我已经在操作会话以添加身份验证数据。
有关更多信息,请查看类似主题的答案:/sf/answers/3027509341/
要在用户登录后立即添加会话标头,您可以将代码基于此主题(通过创建实现 AuthenticationSuccessHandler 的 spring 组件):Spring Security。身份验证后重定向到受保护页面
| 归档时间: |
|
| 查看次数: |
35870 次 |
| 最近记录: |