版权声明: https://blog.csdn.net/weixin_36171229/article/details/80954716
回想一下,Spring cloud微服务框架曾使用两年之久,为什么以前没有这种情况发生呢?
仔细梳理了以前使用的场景,用户在请求业务服务之前,必须先进行系统登录,在用户登录校验请求的时候,创建系统Session而且这种登录校验过程中不涉及跨服务使用Session的情况,在用户登录校验通过以后,用户再请求业务时其实Session已经创建好了,不涉及Session创建,故没有触发上面的情况,的确实际业务使用中很少涉及这种情况,也就让人觉得使用很顺利,一切OK的错觉。
填补这个坑,想到了如下两种方式:
也就是业务上规避一下,如果请求发现Session没有创建,说明用户可能没有登录过,可以创建Session后,将其请求重定向到Home页面或登录页面,这样下次业务数据请求时就可以直接使用Session了。这也是以前使用中没有注意到这个问题原因。
在此感谢HUIQQ0927提供的另外一种更便捷的方式,直接将默认的Cookie中的SESSIONID覆盖掉,重新设置新生成的SESSION ID。如下所示:
public Object run() throws ZuulException { Double rand = Math.random() * 100; int randInt = rand.intValue(); RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); HttpSession session = request.getSession(); if(!request.isRequestedSessionIdValid()) { String sessionBase64 = Base64.getEncoder().encodeToString(session.getId().getBytes()); ctx.addZuulRequestHeader("Cookie", "SESSION=" + sessionBase64); LOG.info("Session Base64:{}",sessionBase64); // ctx.addZuulRequestHeader(SESSION_ID, session.getId()); } session.setAttribute("test", randInt); LOG.info("Session id:{} test:{}",session.getId(),randInt); return null; }
不过需要注意的是这个处理将整个Request请求中的Cookie都覆盖了,通常我们请求中Cookie中不仅仅存放一个SESSION ID,还会有其他业务或如SSO其他系统共享的Cookie,这样处理起来就会比较麻烦,需要先获取原有Request的Cookie获取到,然后修改或添加SESSION ID到原有的Cookie,最后再设置到请求的Header中。
由于是演示,故上面这段代码没有处理原有Cookie信息,实际应用中需要特别慎重处理,否则将引起不必要的麻烦。
Spring boot在请求进来时对Request进行了包装,而在获取Session id时目前Spring boot支持两种,一种Cookie方式,也就是通常默认使用的方式;还有一种是header模式,通过request.getHeader()获取Session ID。
Spring boot定义了Session id获取的接口org.springframework.session.web.http.HttpSessionIdResolver,默认的两个实现类为:
该接口为默认的Spring boot获取Session方法,从Cookie中获取相应的Session id。
2. org.springframework.session.web.http.HeaderHttpSessionIdResolver
该接口通过从Request Header中获取session id,对其感兴趣的话可以查询Spring boot源码进行查看详细内容。
既然Spring boot提供了这个接口,那我们就可以自定义实现一个HttpSessionIdResolver接口,来获取自定义的Session id,下面我们应该怎么实现这个接口呢?
Spring boot默认使用CookieHttpSessionIdResolver进行操作,这样我们就有了参考,因为该类为Final类,不能够继承,故需要将其源码Copy到自定义类中,然后修改其id获取方式,那我们应该以什么方法获取到Session id呢?
我们可以将Header与Cookie两种结合方式来获取,即在Session创建的服务类中,将Session id设置到request header中,而在使用Session的服务类中,定义CustomHttpSessionIdResolver类,先从request header中获取session id,如果获取不到,再从cookie中获取,这样保证了Session id任何时候都能获取到,然后通过Session id也能获取到正确的Session对象了。
具体实现如下:
ZUUL服务中Filter中如果发现创建了Session则将Session id设置到request header中.
@Componentpublic class LoginFilter extends ZuulFilter { private final static Logger LOG = LoggerFactory.getLogger(LoginFilter.class); private final static String SESSION_ID = "SESSIONID"; @Override public Object run() throws ZuulException { Double rand = Math.random() * 100; int randInt = rand.intValue(); RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); HttpSession session = request.getSession(); if(!request.isRequestedSessionIdValid()) { ctx.addZuulRequestHeader(SESSION_ID, session.getId()); } session.setAttribute("test", randInt); LOG.info("Session id:{} test:{}",session.getId(),randInt); return null; } @Override public boolean shouldFilter() { return true; } @Override public int filterOrder() { return 0; } @Override public String filterType() { return "pre"; } }
业务服务中,定制获取Cookie的类,如下:
package com.king.business; import java.util.Collections;import java.util.List; import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils;import org.springframework.session.web.http.CookieHttpSessionIdResolver;import org.springframework.session.web.http.CookieSerializer;import org.springframework.session.web.http.DefaultCookieSerializer;import org.springframework.session.web.http.HttpSessionIdResolver;import org.springframework.session.web.http.CookieSerializer.CookieValue;import org.springframework.stereotype.Component;/** * * @author Administrator * */@Componentpublic class CustomHttpSessionIdResolver implements HttpSessionIdResolver { private static final String WRITTEN_SESSION_ID_ATTR = CookieHttpSessionIdResolver.class .getName().concat(".WRITTEN_SESSION_ID_ATTR"); private final static String SESSION_ID = "SESSIONID"; private CookieSerializer cookieSerializer = new DefaultCookieSerializer(); @Override public List<String> resolveSessionIds(HttpServletRequest request) { String sessionId = request.getHeader(SESSION_ID); if(!StringUtils.isEmpty(sessionId)) { return Collections.singletonList(sessionId); } return this.cookieSerializer.readCookieValues(request); } @Override public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) { if (sessionId.equals(request.getAttribute(WRITTEN_SESSION_ID_ATTR))) { return; } request.setAttribute(WRITTEN_SESSION_ID_ATTR, sessionId); this.cookieSerializer .writeCookieValue(new CookieValue(request, response, sessionId)); } @Override public void expireSession(HttpServletRequest request, HttpServletResponse response) { this.cookieSerializer.writeCookieValue(new CookieValue(request, response, "")); } /** * Sets the {@link CookieSerializer} to be used. * * @param cookieSerializer the cookieSerializer to set. Cannot be null. */ public void setCookieSerializer(CookieSerializer cookieSerializer) { if (cookieSerializer == null) { throw new IllegalArgumentException("cookieSerializer cannot be null"); } this.cookieSerializer = cookieSerializer; } }
这样通过配合完美的解决了Spring cloud单次服务Session传递问题。
Original url: Access
Created at: 2019-03-07 23:34:56
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论