如何将 SameSite 和 Secure 属性设置为 JSESSIONID cookie
How to set SameSite and Secure attribute to JSESSIONID cookie
我在 Apache Tomcat 8.5.5 服务器中有一个 Spring 引导 Web 应用程序(Spring 引导版本 2.0.3.RELEASE)和 运行 .
根据 Google Chrome(自 80.0 起推出)实施的最新安全策略,要求应用新的 SameSite
属性以使 Cross-site 以更安全的方式而不是 CSRF 访问 cookie。因为我没有做任何相关的事情并且 Chrome 已经为 first-party cookies 设置了默认值 SameSite=Lax
,我的一个 third-party 服务集成失败了,原因是 chrome 在 SameSite=Lax
时限制对 cross-site cookie 的访问,并且如果第三方响应来自 POST
请求(O 一旦程序完成 third-party 服务通过 POST
请求) 重定向到我们的网站。在那里 Tomcat 找不到 session 所以它在末尾附加了一个新的 JSESSIONID
(有一个新的 session 并且之前的 session 被杀死了) URL。所以 Spring 拒绝 URL 因为它包含一个由新的 JSESSIONID
附加项引入的分号。
所以我需要更改 JSESSIONID
cookie 属性 (SameSite=None; Secure
) 并尝试了多种方式,包括 WebFilters.I 在 Whosebug 中看到了相同的问题和答案并尝试了大部分他们却无处可去。
有人可以想出一个解决方案来更改 Spring 引导中的那些 headers 吗?
我之前也遇到过同样的情况。由于 javax.servlet.http.Cookie
class 中没有类似 SameSite
的内容,因此无法添加。
第 1 部分: 所以我写了一个过滤器,它只拦截所需的第三方请求。
public class CustomFilter implements Filter {
private static final String THIRD_PARTY_URI = "/third/party/uri";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if(THIRD_PARTY_URI.equals(request.getRequestURI())) {
chain.doFilter(request, new CustomHttpServletResponseWrapper(response));
} else {
chain.doFilter(request, response);
}
}
enter code here
// ... init destroy methods here
}
第 2 部分: Cookie 作为 Set-Cookie
响应 header 发送。所以这个 CustomHttpServletResponseWrapper
覆盖 addCookie
方法并检查,如果它是所需的 cookie (JSESSIONID
),而不是将它添加到 cookie,它直接添加到响应 header Set-Cookie
具有 SameSite=None
属性。
public class CustomHttpServletResponseWrapper extends HttpServletResponseWrapper {
public CustomHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public void addCookie(Cookie cookie) {
if ("JSESSIONID".equals(cookie.getName())) {
super.addHeader("Set-Cookie", getCookieValue(cookie));
} else {
super.addCookie(cookie);
}
}
private String getCookieValue(Cookie cookie) {
StringBuilder builder = new StringBuilder();
builder.append(cookie.getName()).append('=').append(cookie.getValue());
builder.append(";Path=").append(cookie.getPath());
if (cookie.isHttpOnly()) {
builder.append(";HttpOnly");
}
if (cookie.getSecure()) {
builder.append(";Secure");
}
// here you can append other attributes like domain / max-age etc.
builder.append(";SameSite=None");
return builder.toString();
}
}
UPDATE 于 06/07/2021 - 添加了正确的 Path
属性和新的 sameSite 属性以避免 session cookie 重复GenericFilterBean
方法。
我能够为此想出自己的解决方案。
我有两种应用程序 运行 在 Spring 启动时具有不同的 Spring 安全配置,他们需要不同的解决方案来解决这个问题。
案例 1:无用户身份验证
解决方案 1
在这里,您可能已经在您的应用程序中为第 3 方响应创建了一个端点。在控制器方法中访问 httpSession 之前,您是安全的。如果您在不同的控制器方法中访问 session,请向那里发送一个临时重定向请求,如下所示。
@Controller
public class ThirdPartyResponseController{
@RequestMapping(value=3rd_party_response_URL, method=RequestMethod.POST)
public void thirdPartyresponse(HttpServletRequest request, HttpServletResponse httpServletResponse){
// your logic
// and you can set any data as an session attribute which you want to access over the 2nd controller
request.getSession().setAttribute(<data>)
try {
httpServletResponse.sendRedirect(<redirect_URL>);
} catch (IOException e) {
// handle error
}
}
@RequestMapping(value=redirect_URL, method=RequestMethod.GET)
public String thirdPartyresponse(HttpServletRequest request, HttpServletResponse httpServletResponse, Model model, RedirectAttributes redirectAttributes, HttpSession session){
// your logic
return <to_view>;
}
}
不过,您需要在安全配置中允许 3rd_party_response_url。
解决方案 2
您可以尝试下面描述的相同 GenericFilterBean
方法。
情况2:用户需要authenticated/sign在
在您通过 HttpSecurity or WebSecurity 配置了大部分安全规则的 Spring Web 应用程序中,检查此解决方案。
我测试过解决方案的示例安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
......
..antMatchers(<3rd_party_response_URL>).permitAll();
.....
..csrf().ignoringAntMatchers(<3rd_party_response_URL>);
}
}
我想在此配置中强调的要点是您应该允许来自 Spring 安全和 CSRF 保护(如果已启用)的第 3 方响应 URL。
然后我们需要创建一个 HttpServletRequest 过滤器,方法是扩展 GenericFilterBean class (Filter class 对我不起作用)并通过拦截将 SameSite
属性设置为 JSESSIONID
cookie每个 HttpServletRequest
并设置响应 headers.
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class SessionCookieFilter extends GenericFilterBean {
private final List<String> PATHS_TO_IGNORE_SETTING_SAMESITE = Arrays.asList("resources", <add other paths you want to exclude>);
private final String SESSION_COOKIE_NAME = "JSESSIONID";
private final String SESSION_PATH_ATTRIBUTE = ";Path=";
private final String ROOT_CONTEXT = "/";
private final String SAME_SITE_ATTRIBUTE_VALUES = ";HttpOnly;Secure;SameSite=None";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String requestUrl = req.getRequestURL().toString();
boolean isResourceRequest = requestUrl != null ? StringUtils.isNoneBlank(PATHS_TO_IGNORE_SETTING_SAMESITE.stream().filter(s -> requestUrl.contains(s)).findFirst().orElse(null)) : null;
if (!isResourceRequest) {
Cookie[] cookies = ((HttpServletRequest) request).getCookies();
if (cookies != null && cookies.length > 0) {
List<Cookie> cookieList = Arrays.asList(cookies);
Cookie sessionCookie = cookieList.stream().filter(cookie -> SESSION_COOKIE_NAME.equals(cookie.getName())).findFirst().orElse(null);
if (sessionCookie != null) {
String contextPath = request.getServletContext() != null && StringUtils.isNotBlank(request.getServletContext().getContextPath()) ? request.getServletContext().getContextPath() : ROOT_CONTEXT;
resp.setHeader(HttpHeaders.SET_COOKIE, sessionCookie.getName() + "=" + sessionCookie.getValue() + SESSION_PATH_ATTRIBUTE + contextPath + SAME_SITE_ATTRIBUTE_VALUES);
}
}
}
chain.doFilter(request, response);
}
}
然后通过
将此过滤器添加到 Spring 安全过滤器链
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
....
.addFilterAfter(new SessionCookieFilter(), BasicAuthenticationFilter.class);
}
为了确定您需要将新过滤器放置在 Spring 的安全过滤器链中的什么位置,您可以 debug the Spring security filter chain easily and identify a proper location in the filter chain. Apart from the BasicAuthenticationFilter
, after the SecurityContextPersistanceFilter 将是另一个理想的位置。
此 SameSite
cookie 属性将不支持某些 old browser versions,在这种情况下,请检查浏览器并避免在不兼容的客户端中设置 SameSite
。
private static final String _I_PHONE_IOS_12 = "iPhone OS 12_";
private static final String _I_PAD_IOS_12 = "iPad; CPU OS 12_";
private static final String _MAC_OS_10_14 = " OS X 10_14_";
private static final String _VERSION = "Version/";
private static final String _SAFARI = "Safari";
private static final String _EMBED_SAFARI = "(KHTML, like Gecko)";
private static final String _CHROME = "Chrome/";
private static final String _CHROMIUM = "Chromium/";
private static final String _UC_BROWSER = "UCBrowser/";
private static final String _ANDROID = "Android";
/*
* checks SameSite=None;Secure incompatible Browsers
* https://www.chromium.org/updates/same-site/incompatible-clients
*/
public static boolean isSameSiteInCompatibleClient(HttpServletRequest request) {
String userAgent = request.getHeader("user-agent");
if (StringUtils.isNotBlank(userAgent)) {
boolean isIos12 = isIos12(userAgent), isMacOs1014 = isMacOs1014(userAgent), isChromeChromium51To66 = isChromeChromium51To66(userAgent), isUcBrowser = isUcBrowser(userAgent);
//TODO : Added for testing purpose. remove before Prod release.
LOG.info("*********************************************************************************");
LOG.info("is iOS 12 = {}, is MacOs 10.14 = {}, is Chrome 51-66 = {}, is Android UC Browser = {}", isIos12, isMacOs1014, isChromeChromium51To66, isUcBrowser);
LOG.info("*********************************************************************************");
return isIos12 || isMacOs1014 || isChromeChromium51To66 || isUcBrowser;
}
return false;
}
private static boolean isIos12(String userAgent) {
return StringUtils.contains(userAgent, _I_PHONE_IOS_12) || StringUtils.contains(userAgent, _I_PAD_IOS_12);
}
private static boolean isMacOs1014(String userAgent) {
return StringUtils.contains(userAgent, _MAC_OS_10_14)
&& ((StringUtils.contains(userAgent, _VERSION) && StringUtils.contains(userAgent, _SAFARI)) //Safari on MacOS 10.14
|| StringUtils.contains(userAgent, _EMBED_SAFARI)); // Embedded browser on MacOS 10.14
}
private static boolean isChromeChromium51To66(String userAgent) {
boolean isChrome = StringUtils.contains(userAgent, _CHROME), isChromium = StringUtils.contains(userAgent, _CHROMIUM);
if (isChrome || isChromium) {
int version = isChrome ? Integer.valueOf(StringUtils.substringAfter(userAgent, _CHROME).substring(0, 2))
: Integer.valueOf(StringUtils.substringAfter(userAgent, _CHROMIUM).substring(0, 2));
return ((version >= 51) && (version <= 66)); //Chrome or Chromium V51-66
}
return false;
}
private static boolean isUcBrowser(String userAgent) {
if (StringUtils.contains(userAgent, _UC_BROWSER) && StringUtils.contains(userAgent, _ANDROID)) {
String[] version = StringUtils.splitByWholeSeparator(StringUtils.substringAfter(userAgent, _UC_BROWSER).substring(0, 7), ".");
int major = Integer.valueOf(version[0]), minor = Integer.valueOf(version[1]), build = Integer.valueOf(version[2]);
return ((major != 0) && ((major < 12) || (major == 12 && (minor < 13)) || (major == 12 && minor == 13 && (build < 2)))); //UC browser below v12.13.2 in android
}
return false;
}
在 SessionCookieFilter 中添加上面的检查,如下所示,
if (!isResourceRequest && !UserAgentUtils.isSameSiteInCompatibleClient(req)) {
此过滤器在本地主机环境中不起作用,因为它需要安全 (HTTPS) 连接来设置 Secure
cookie 属性。
有关详细说明,请阅读此 blog post。
如本回答所述:
Same-Site flag for session cookie in Spring Security
@Configuration
public static class WebConfig implements WebMvcConfigurer {
@Bean
public TomcatContextCustomizer sameSiteCookiesConfig() {
return context -> {
final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
context.setCookieProcessor(cookieProcessor);
};
}
}
但这似乎更简单
@Configuration
public static class WebConfig implements WebMvcConfigurer {
@Bean
public CookieSameSiteSupplier cookieSameSiteSupplier(){
return CookieSameSiteSupplier.ofNone();
}
}
或者...更简单,spring boot since 2.6.0 supports setting it in application.properties.
Spring documentation about SameSite Cookies
server.servlet.session.cookie.same-site = none
我在 Apache Tomcat 8.5.5 服务器中有一个 Spring 引导 Web 应用程序(Spring 引导版本 2.0.3.RELEASE)和 运行 .
根据 Google Chrome(自 80.0 起推出)实施的最新安全策略,要求应用新的 SameSite
属性以使 Cross-site 以更安全的方式而不是 CSRF 访问 cookie。因为我没有做任何相关的事情并且 Chrome 已经为 first-party cookies 设置了默认值 SameSite=Lax
,我的一个 third-party 服务集成失败了,原因是 chrome 在 SameSite=Lax
时限制对 cross-site cookie 的访问,并且如果第三方响应来自 POST
请求(O 一旦程序完成 third-party 服务通过 POST
请求) 重定向到我们的网站。在那里 Tomcat 找不到 session 所以它在末尾附加了一个新的 JSESSIONID
(有一个新的 session 并且之前的 session 被杀死了) URL。所以 Spring 拒绝 URL 因为它包含一个由新的 JSESSIONID
附加项引入的分号。
所以我需要更改 JSESSIONID
cookie 属性 (SameSite=None; Secure
) 并尝试了多种方式,包括 WebFilters.I 在 Whosebug 中看到了相同的问题和答案并尝试了大部分他们却无处可去。
有人可以想出一个解决方案来更改 Spring 引导中的那些 headers 吗?
我之前也遇到过同样的情况。由于 javax.servlet.http.Cookie
class 中没有类似 SameSite
的内容,因此无法添加。
第 1 部分: 所以我写了一个过滤器,它只拦截所需的第三方请求。
public class CustomFilter implements Filter {
private static final String THIRD_PARTY_URI = "/third/party/uri";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if(THIRD_PARTY_URI.equals(request.getRequestURI())) {
chain.doFilter(request, new CustomHttpServletResponseWrapper(response));
} else {
chain.doFilter(request, response);
}
}
enter code here
// ... init destroy methods here
}
第 2 部分: Cookie 作为 Set-Cookie
响应 header 发送。所以这个 CustomHttpServletResponseWrapper
覆盖 addCookie
方法并检查,如果它是所需的 cookie (JSESSIONID
),而不是将它添加到 cookie,它直接添加到响应 header Set-Cookie
具有 SameSite=None
属性。
public class CustomHttpServletResponseWrapper extends HttpServletResponseWrapper {
public CustomHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public void addCookie(Cookie cookie) {
if ("JSESSIONID".equals(cookie.getName())) {
super.addHeader("Set-Cookie", getCookieValue(cookie));
} else {
super.addCookie(cookie);
}
}
private String getCookieValue(Cookie cookie) {
StringBuilder builder = new StringBuilder();
builder.append(cookie.getName()).append('=').append(cookie.getValue());
builder.append(";Path=").append(cookie.getPath());
if (cookie.isHttpOnly()) {
builder.append(";HttpOnly");
}
if (cookie.getSecure()) {
builder.append(";Secure");
}
// here you can append other attributes like domain / max-age etc.
builder.append(";SameSite=None");
return builder.toString();
}
}
UPDATE 于 06/07/2021 - 添加了正确的 Path
属性和新的 sameSite 属性以避免 session cookie 重复GenericFilterBean
方法。
我能够为此想出自己的解决方案。
我有两种应用程序 运行 在 Spring 启动时具有不同的 Spring 安全配置,他们需要不同的解决方案来解决这个问题。
案例 1:无用户身份验证
解决方案 1
在这里,您可能已经在您的应用程序中为第 3 方响应创建了一个端点。在控制器方法中访问 httpSession 之前,您是安全的。如果您在不同的控制器方法中访问 session,请向那里发送一个临时重定向请求,如下所示。
@Controller
public class ThirdPartyResponseController{
@RequestMapping(value=3rd_party_response_URL, method=RequestMethod.POST)
public void thirdPartyresponse(HttpServletRequest request, HttpServletResponse httpServletResponse){
// your logic
// and you can set any data as an session attribute which you want to access over the 2nd controller
request.getSession().setAttribute(<data>)
try {
httpServletResponse.sendRedirect(<redirect_URL>);
} catch (IOException e) {
// handle error
}
}
@RequestMapping(value=redirect_URL, method=RequestMethod.GET)
public String thirdPartyresponse(HttpServletRequest request, HttpServletResponse httpServletResponse, Model model, RedirectAttributes redirectAttributes, HttpSession session){
// your logic
return <to_view>;
}
}
不过,您需要在安全配置中允许 3rd_party_response_url。
解决方案 2
您可以尝试下面描述的相同 GenericFilterBean
方法。
情况2:用户需要authenticated/sign在
在您通过 HttpSecurity or WebSecurity 配置了大部分安全规则的 Spring Web 应用程序中,检查此解决方案。
我测试过解决方案的示例安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
......
..antMatchers(<3rd_party_response_URL>).permitAll();
.....
..csrf().ignoringAntMatchers(<3rd_party_response_URL>);
}
}
我想在此配置中强调的要点是您应该允许来自 Spring 安全和 CSRF 保护(如果已启用)的第 3 方响应 URL。
然后我们需要创建一个 HttpServletRequest 过滤器,方法是扩展 GenericFilterBean class (Filter class 对我不起作用)并通过拦截将 SameSite
属性设置为 JSESSIONID
cookie每个 HttpServletRequest
并设置响应 headers.
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class SessionCookieFilter extends GenericFilterBean {
private final List<String> PATHS_TO_IGNORE_SETTING_SAMESITE = Arrays.asList("resources", <add other paths you want to exclude>);
private final String SESSION_COOKIE_NAME = "JSESSIONID";
private final String SESSION_PATH_ATTRIBUTE = ";Path=";
private final String ROOT_CONTEXT = "/";
private final String SAME_SITE_ATTRIBUTE_VALUES = ";HttpOnly;Secure;SameSite=None";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String requestUrl = req.getRequestURL().toString();
boolean isResourceRequest = requestUrl != null ? StringUtils.isNoneBlank(PATHS_TO_IGNORE_SETTING_SAMESITE.stream().filter(s -> requestUrl.contains(s)).findFirst().orElse(null)) : null;
if (!isResourceRequest) {
Cookie[] cookies = ((HttpServletRequest) request).getCookies();
if (cookies != null && cookies.length > 0) {
List<Cookie> cookieList = Arrays.asList(cookies);
Cookie sessionCookie = cookieList.stream().filter(cookie -> SESSION_COOKIE_NAME.equals(cookie.getName())).findFirst().orElse(null);
if (sessionCookie != null) {
String contextPath = request.getServletContext() != null && StringUtils.isNotBlank(request.getServletContext().getContextPath()) ? request.getServletContext().getContextPath() : ROOT_CONTEXT;
resp.setHeader(HttpHeaders.SET_COOKIE, sessionCookie.getName() + "=" + sessionCookie.getValue() + SESSION_PATH_ATTRIBUTE + contextPath + SAME_SITE_ATTRIBUTE_VALUES);
}
}
}
chain.doFilter(request, response);
}
}
然后通过
将此过滤器添加到 Spring 安全过滤器链@Override
protected void configure(HttpSecurity http) throws Exception {
http.
....
.addFilterAfter(new SessionCookieFilter(), BasicAuthenticationFilter.class);
}
为了确定您需要将新过滤器放置在 Spring 的安全过滤器链中的什么位置,您可以 debug the Spring security filter chain easily and identify a proper location in the filter chain. Apart from the BasicAuthenticationFilter
, after the SecurityContextPersistanceFilter 将是另一个理想的位置。
此 SameSite
cookie 属性将不支持某些 old browser versions,在这种情况下,请检查浏览器并避免在不兼容的客户端中设置 SameSite
。
private static final String _I_PHONE_IOS_12 = "iPhone OS 12_";
private static final String _I_PAD_IOS_12 = "iPad; CPU OS 12_";
private static final String _MAC_OS_10_14 = " OS X 10_14_";
private static final String _VERSION = "Version/";
private static final String _SAFARI = "Safari";
private static final String _EMBED_SAFARI = "(KHTML, like Gecko)";
private static final String _CHROME = "Chrome/";
private static final String _CHROMIUM = "Chromium/";
private static final String _UC_BROWSER = "UCBrowser/";
private static final String _ANDROID = "Android";
/*
* checks SameSite=None;Secure incompatible Browsers
* https://www.chromium.org/updates/same-site/incompatible-clients
*/
public static boolean isSameSiteInCompatibleClient(HttpServletRequest request) {
String userAgent = request.getHeader("user-agent");
if (StringUtils.isNotBlank(userAgent)) {
boolean isIos12 = isIos12(userAgent), isMacOs1014 = isMacOs1014(userAgent), isChromeChromium51To66 = isChromeChromium51To66(userAgent), isUcBrowser = isUcBrowser(userAgent);
//TODO : Added for testing purpose. remove before Prod release.
LOG.info("*********************************************************************************");
LOG.info("is iOS 12 = {}, is MacOs 10.14 = {}, is Chrome 51-66 = {}, is Android UC Browser = {}", isIos12, isMacOs1014, isChromeChromium51To66, isUcBrowser);
LOG.info("*********************************************************************************");
return isIos12 || isMacOs1014 || isChromeChromium51To66 || isUcBrowser;
}
return false;
}
private static boolean isIos12(String userAgent) {
return StringUtils.contains(userAgent, _I_PHONE_IOS_12) || StringUtils.contains(userAgent, _I_PAD_IOS_12);
}
private static boolean isMacOs1014(String userAgent) {
return StringUtils.contains(userAgent, _MAC_OS_10_14)
&& ((StringUtils.contains(userAgent, _VERSION) && StringUtils.contains(userAgent, _SAFARI)) //Safari on MacOS 10.14
|| StringUtils.contains(userAgent, _EMBED_SAFARI)); // Embedded browser on MacOS 10.14
}
private static boolean isChromeChromium51To66(String userAgent) {
boolean isChrome = StringUtils.contains(userAgent, _CHROME), isChromium = StringUtils.contains(userAgent, _CHROMIUM);
if (isChrome || isChromium) {
int version = isChrome ? Integer.valueOf(StringUtils.substringAfter(userAgent, _CHROME).substring(0, 2))
: Integer.valueOf(StringUtils.substringAfter(userAgent, _CHROMIUM).substring(0, 2));
return ((version >= 51) && (version <= 66)); //Chrome or Chromium V51-66
}
return false;
}
private static boolean isUcBrowser(String userAgent) {
if (StringUtils.contains(userAgent, _UC_BROWSER) && StringUtils.contains(userAgent, _ANDROID)) {
String[] version = StringUtils.splitByWholeSeparator(StringUtils.substringAfter(userAgent, _UC_BROWSER).substring(0, 7), ".");
int major = Integer.valueOf(version[0]), minor = Integer.valueOf(version[1]), build = Integer.valueOf(version[2]);
return ((major != 0) && ((major < 12) || (major == 12 && (minor < 13)) || (major == 12 && minor == 13 && (build < 2)))); //UC browser below v12.13.2 in android
}
return false;
}
在 SessionCookieFilter 中添加上面的检查,如下所示,
if (!isResourceRequest && !UserAgentUtils.isSameSiteInCompatibleClient(req)) {
此过滤器在本地主机环境中不起作用,因为它需要安全 (HTTPS) 连接来设置 Secure
cookie 属性。
有关详细说明,请阅读此 blog post。
如本回答所述: Same-Site flag for session cookie in Spring Security
@Configuration
public static class WebConfig implements WebMvcConfigurer {
@Bean
public TomcatContextCustomizer sameSiteCookiesConfig() {
return context -> {
final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
context.setCookieProcessor(cookieProcessor);
};
}
}
但这似乎更简单
@Configuration
public static class WebConfig implements WebMvcConfigurer {
@Bean
public CookieSameSiteSupplier cookieSameSiteSupplier(){
return CookieSameSiteSupplier.ofNone();
}
}
或者...更简单,spring boot since 2.6.0 supports setting it in application.properties.
Spring documentation about SameSite Cookies
server.servlet.session.cookie.same-site = none