Java单点登录 跨域时SameSite导致Cookie失败问题
单点登录跨域碰上SameSite问题
项目场景:
两个项目:auth_webapp、admin_webapp
单点登录流程:
- 点击登录请求:/auth/api/login?name=xxx&password=xxx,auth服务器用jwt处理后获得token,将token写入cookie,并返回token。
- 前端请求:/admin/api/getLoginVo,admin服务器返回提示未登录。
- 前端再次请求:/auth/api/login,此时不带用户名和密码,只是将1中的cookie带入request。
- 前端拿到3中的token请求:/admin/api/login?token=xxx,admin服务器验证token,返回登录结果。
- 前端再次请求:/admin/api/getLoginVo,admin服务器返回登录用户信息。
问题描述:
最近发现个别用户总是登录不了,通过分析请求发现,在流程3时,cookie没有写入request请求中,导致再次获取token失败,从而流程断裂。
原因分析:
百度SameSite可以知道是浏览器用来限制第三方cookie的,具体可以参考
http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html
正是由于Chrome更改了默认的SameSite属性,导致了在流程3时,不能将cookie写入request中。
解决方案:
既然明白了是SameSite属性导致了跨域cookie不能被使用,那么我们就将cookie的SameSite属性改成最低级的就可以解决这个问题了。在auth服务器中添加Interceptor:
public class SameSiteInterceptor implements HandlerInterceptor { @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 { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE); boolean firstHeader = true; for (String header : headers) { // there can be multiple Set-Cookie attributes response.addHeader(HttpHeaders.SET_COOKIE, String.format("%s; %s", header, "SameSite=None; Secure;")); } } }
这里还遇到一个问题:headers一直是空?
之前auth_webapp的容器使用的是undertow,headers一直获取不到,之后切换成了tomcat又可以了。
源码分析:
undertow的HttpServletResponse实现类是io.undertow.servlet.spec.HttpServletResponseImpl,源码分析:
public final class HttpServletResponseImpl implements HttpServletResponse { private final HttpServerExchange exchange; private final ServletContextImpl originalServletContext; private volatile ServletContextImpl servletContext; //此处省略无用代码 @Override public void addCookie(final Cookie cookie) { if (insideInclude) { return; } final ServletCookieAdaptor servletCookieAdaptor = new ServletCookieAdaptor(cookie); if (cookie.getVersion() == 0) { servletCookieAdaptor.setVersion(servletContext.getDeployment().getDeploymentInfo().getDefaultCookieVersion()); } exchange.setResponseCookie(servletCookieAdaptor); } }
从源码可以看出,addCookie方法其实是将cookie保存在了exchange对象里面。而没有放入header中。对比而言,我们可以看看tomcat中的HttpServletResponse实现类org.apache.catalina.connector.ResponseFacade
@Override public void addCookie(Cookie cookie) { if (isCommitted()) { return; } response.addCookie(cookie); }
继续追org.apache.catalina.connector.Response.addCookie()
/**
* Add the specified Cookie to those that will be included with
* this Response.
*
* @param cookie Cookie to be added
*/ @Override public void addCookie(final Cookie cookie) { // Ignore any call from an included servlet if (included || isCommitted()) { return; } cookies.add(cookie); String header = generateCookieString(cookie); //if we reached here, no exception, cookie is valid // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 ) // RFC2965 is not supported by browsers and the Servlet spec // asks for 2109. addHeader("Set-Cookie", header, getContext().getCookieProcessor().getCharset()); }
本文地址:https://blog.csdn.net/y1378503395/article/details/108236630
上一篇: 保护儿童视力 哪些不当行为损害孩子视力