一、SameSite Cookie 简介
SameSite-cookies是一种机制,用于定义cookie如何跨域发送。这是谷歌开发的一种安全机制,并且现在在最新版本(Chrome Dev 51.0.2704.4)中已经开始实行了。SameSite-cookies的目的是尝试阻止CSRF(Cross-site request forgery 跨站请求伪造)以及XSSI(Cross Site Script Inclusion (XSSI) 跨站脚本包含)攻击。
二、语法
语法是SameSite=<value>, 例如SameSite=Lax
- Strict
Strict是最严格的防护,有能力阻止所有CSRF攻击。然而,它的用户友好性太差,因为它可能会将所有GET请求进行CSRF防护处理。
例如:一个用户在reddit.com点击了一个链接(GET请求),这个链接是到facebook.com的,而假如facebook.com使用了Samesite-cookies并且将值设置为了Strict,那么用户将不能登陆Facebook.com,因为在Strict情况下,浏览器不允许将cookie从A域发送到B域。 - Lax
Lax(relax的缩写?)属性只会在使用危险HTTP方法发送跨域cookie的时候进行阻止,例如POST方式。
例1:一个用户在reddit.com点击了一个链接(GET请求),这个链接是到facebook.com的,而假如facebook.com使用了Samesite-cookies并且将值设置为了Lax,那么用户可以正常登录facebok.com,因为浏览器允许将cookie从A域发送到B域。
例2:一个用户在reddit.com提交了一个表单(POST请求),这个表单是提交到facebook.com的,而假如facebook.com使用了Samesite-cookies并且将值设置为了Lax,那么用户将不能正常登陆Facebook.com,因为浏览器不允许使用POST方式将cookie从A域发送到B域。
三、SpringWeb
spring web 最新版默认生成为SameSite=Lax,奇怪的是用spring data Session redis 后 cookie新增了 SameSite这个字段,所以不能携带cookie进行跨域post访问,文档上也不表明什么时候开始的,坑的是默认为Lax也不能设置,遂现在将web版本降级。
因为服务端返回给客户端的set-cookie中带有samesite=lax,这就是问题的根源,它表示不能携带cookie进行跨域post访问,然而我们是需要携带cookie的。
解决办法:
- 后端配置
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@Bean
public CookieSerializer httpSessionIdResolver() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
// 取消仅限同一站点设置
cookieSerializer.setSameSite(null);
return cookieSerializer;
}
并设置:
response.setHeader("Access-Control-Allow-Credentials", "true");
或
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedOrigin("http://localhost:8088");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true); // true代表允许携带cookie
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// “*”代表全部。”**”代表适配所有接口。
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
- 前端ajax请求:
添加:
xhrFields: {
withCredentials: true
}