是否可以在Spring Security中设置Same-site Cookie标志? 请参阅:https://tools.ietf.org/html/draft-west-first-party-cookies-07 如果没有,请在路线图上添加支持吗?某些浏览器(即Chrome)已经支持。 T.H。
答案 0 :(得分:7)
New Tomcat version通过TomcatContextCustomizer
支持SameSite cookie。因此,您应该仅自定义tomcat CookieProcessor,例如对于Spring Boot:
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Bean
public TomcatContextCustomizer sameSiteCookiesConfig() {
return context -> {
final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
context.setCookieProcessor(cookieProcessor);
};
}
}
请SameSiteCookies.NONE
注意,cookie也是Secure
(使用SSL),否则它们将无法应用。
默认情况下,因为Chrome 80 cookie被视为SameSite=Lax
!
请参见SameSite Cookie in Spring Boot和SameSite cookie recipes。
对于nginx代理,可以在nginx配置中轻松解决:
if ($scheme = http) {
return 301 https://$http_host$request_uri;
}
proxy_cookie_path / "/; secure; SameSite=None";
答案 1 :(得分:5)
不可能。 Spring Session中支持此功能:https://spring.io/blog/2018/10/31/spring-session-bean-ga-released
我想出了一种类似于罗恩(Ron)的解决方案。但是有一件重要的事情要注意:
用于跨站点使用的Cookie必须指定
SameSite=None; Secure
以便将其包含在第三方环境中。
因此,我在标题中添加了安全属性。另外,不用时不必覆盖所有三种方法。仅在实现HandlerInterceptor
时才需要。
import org.apache.commons.lang.StringUtils;
public class CookiesInterceptor extends HandlerInterceptorAdapter {
final String sameSiteAttribute = "; SameSite=None";
final String secureAttribute = "; Secure";
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
addEtagHeader(request, response);
Collection<String> setCookieHeaders = response.getHeaders(HttpHeaders.SET_COOKIE);
if (setCookieHeaders == null || setCookieHeaders.isEmpty())
return;
setCookieHeaders
.stream()
.filter(StringUtils::isNotBlank)
.map(header -> {
if (header.toLowerCase().contains("samesite")) {
return header;
} else {
return header.concat(sameSiteAttribute);
}
})
.map(header -> {
if (header.toLowerCase().contains("secure")) {
return header;
} else {
return header.concat(secureAttribute);
}
})
.forEach(finalHeader -> response.setHeader(HttpHeaders.SET_COOKIE, finalHeader));
}
}
我在项目中使用了xml,因此必须将其添加到配置文件中:
<mvc:interceptors>
<bean class="com.zoetis.widgetserver.mvc.CookiesInterceptor"/>
</mvc:interceptors>
答案 2 :(得分:4)
这里所有可能的解决方案对我来说都是失败的。每次我尝试使用过滤器或拦截器时,尚未添加Set-Cookie标头。我能够完成这项工作的唯一方法是添加Spring Session并将此bean添加到我的@Configuration
文件之一中:
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setSameSite("none");
return serializer;
}
无论如何,希望这对我遇到同样情况的人有所帮助。
答案 3 :(得分:3)
在SpringBoot中使用拦截器。
我正在寻找一种与您添加SameSite的解决方案,我只想将属性添加到现有的“ Set-Cookie”,而不是创建新的“ Set-Cookie”。 我尝试了几种方法来满足此要求,包括:
最后,我发现春季的拦截器可以帮助我做到这一点。 我花了一个星期才得到它。希望如果有人遇到同样的问题,可以为您提供帮助。
@Component
public class CookieServiceInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
//check whether it has "set-cookie" in the response, if it has, then add "SameSite" attribute
//it should be found in the response of the first successful login
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 afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {
}
}
,并且还需要使该拦截器在您的应用程序中工作,这意味着您应该添加如下所示的bean:
@Autowired
CookieServiceInterceptor cookieServiceInterceptor;
@Bean
public MappedInterceptor myInterceptor() {
return new MappedInterceptor(null, cookieServiceInterceptor);
}
此拦截器有一个缺陷,当请求被重定向(例如返回302)或失败(例如返回401)时,它无法添加相同的站点,而在SSO时它使我的应用程序失败。最终,我必须使用Tomcat cookie,因为我没有在我的springboot应用程序中嵌入tomcat。我添加
<Context>
<CookieProcessor sameSiteCookies="none" />
</Context>
在我的应用程序/ META-INF下的context.xml中。它将在每个响应的set-cookie标头中添加SameSite属性。请注意,从Tomcat 9.0.21和8.5.42开始,此行为是可能的。根据{{3}}
答案 4 :(得分:1)
如果您可以获得HttpServletResponse
的实例,则可以随时在Java世界中设置cookie值。
然后你可以这样做:
response.setHeader("Set-Cookie", "key=value; HttpOnly; SameSite=strict")
在spring-security中,您可以使用过滤器轻松完成此操作,这是一个示例:
public class CustomFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse)response;
resp.setHeader("Set-Cookie", "locale=de; HttpOnly; SameSite=strict");
chain.doFilter(request, response);
}
}
将此过滤器添加到您的SecurityConfig,如下所示:
http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class)
或通过XML:
<http>
<custom-filter after="BASIC_AUTH_FILTER" ref="myFilter" />
</http>
<beans:bean id="myFilter" class="org.bla.CustomFilter"/>
答案 5 :(得分:1)
我已经针对没有spring-webmvc
的{{1}}测试了该解决方案-请参阅simple feedback form,但我认为它也适用于spring-security
。
SessionRepositoryFilter
中的spring-session-core bean 您可以使用 spring HttpSession
扩展默认的Java Session
,并用自定义的cookie替换spring-boot
cookie,如下所示:
JSESSIONID
其他 spring Session
cookie标志可以使用DefaultCookieSerializer
设置:
Set-Cookie: JSESSIONID=NWU4NzY4NWUtMDY3MC00Y2M1LTg1YmMtNmE1ZWJmODcxNzRj; Path=/; Secure; HttpOnly; SameSite=None
我扩展了MapSessionRepository的实现,因为它不支持触发SessionDeletedEvent或SessionExpiredEvent -我在添加新会话之前添加了清除过期会话的功能。我认为这对于一个小型应用程序就足够了。
答案 6 :(得分:0)
您可以在身份验证成功处理程序中代替过滤器。
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
clearAuthenticationAttributes(request);
addSameSiteCookieAttribute(response);
handle(request, response);
}
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"));
}
}
答案之一中提到了它。实现链接后找不到链接。
答案 7 :(得分:0)
对于 Spring Webflux(反应式环境),这对我有用:
@Configuration
@EnableSpringWebSession
public class SessionModule {
@Bean
public ReactiveSessionRepository<MapSession> reactiveSessionRepository() {
return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
}
@Bean
public WebSessionIdResolver webSessionIdResolver() {
CookieWebSessionIdResolver resolver = new CookieWebSessionIdResolver();
resolver.setCookieName("SESSION");
resolver.addCookieInitializer((builder) -> {
builder.path("/")
.httpOnly(true)
.secure(true)
.sameSite("None; Secure");
});
return resolver;
}
}
答案 8 :(得分:0)
您可以通过使用 ResponseCookie 并将其添加到您的 HttpServletResponse 中自行添加 cookie。
ResponseCookie cookie = ResponseCookie.from("cookiename", "cookieValue")
.maxAge(3600) // one hour
.domain("test.com")
.sameSite("None")
.secure(true)
.path("/")
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());