本文共 3597 字,大约阅读时间需要 11 分钟。
在springcloud微服务应用中,各微服务按传统方式获取的session是不同的,为实现各微服务共享session,spring-session提供了解决方案,对HttpSession重新实现,并将session存放于redis中,各微服务从redis中获取一致的session对象。
在网关zuul和各微服务中引入如下依赖
org.springframework.boot spring-boot-starter-data-redis org.springframework.session spring-session-data-redis
在网关zuul组件和各微服务组件的配置类(或者启动类)上加注解@EnableRedisHttpSession(flushMode = FlushMode.IMMEDIATE)启用缓存于redis的springsession,其中flushMode = FlushMode.IMMEDIATE表示session新建或属性发生变化时立即持久化,即当session发生变化时各组件立即感知。
配置示例如下:@SpringBootApplication@EnableRedisHttpSession(flushMode = FlushMode.IMMEDIATE)//......public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } //......}
在spring配置文件application.yaml中配置redis,示例如下:
spring: redis: database: 0 host: 127.0.0.1 port: 6379 password: timeout: 1500 jedis: pool: max-idle: 10 max-wait: -1ms min-idle: 2
在网关的spring配置文件中,还需加入如下配置:
zuul: sensitive-headers: #不过滤客户端的任何请求头(如:Cookie不会被过滤) ignored-headers: #微服务之间的传递不过滤任何请求头(如:微服务之间传递Cookie不会被过滤)
经过上述配置已经能够实现非跨域(同源)情况下session的共享。但是,经过在Springboot2.2.x环境下测试,在跨域情况下还是无法实现session共享(这可能与springboot版本有一定关系),经在网上查资料找到原因,这是因为springsession使用CookieSerializer对象设置cookie时候,其默认的同源策略(SameSite)为lax,导致在跨域请求时除get方式外都不会向服务器发送cookie,解决办法是在各微服务中配置一个新的CookieSerializer对象,将同源策略设置为null,即在配置类(或者启动类)中添加如下配置:
@Beanpublic CookieSerializer cookieSerializer() { DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); cookieSerializer.setSameSite(null); return cookieSerializer;}
(此步骤一般没有必要),可能某些版本的springboot或者在某些特殊情况下经过上述配置后,在跨域条件下session可以实现共享了,但是第一次访问session不能共享,第二次及以后的访问session才可以共享。这是是由于第一次访问时,session信息还没有被写入cookie,也就是客户端还没有存储session信息,请求时也就不会向服务器发送包含session信息的cookie,在微服务相互调用过程中也就不会传递cookie,这样每一个微服务都会创建新的session,session也就不共享了,而第一次请求响应时会将包含session信息的cookie发送给客户端,因此以后请求session是可以共享的。
这个问题的解决的办法有多种,比较简单的一个办法是:在访问客户端首页时自动默认向服务器发送第一次无请意义的请求,在服务端也为该请求配置一个不做任何业务处理的映射路径,接下来用户进行操作都会传递含有session信息的cookie了。
当前用户信息是否存在的验证
在实际开发中,需要在每一次请求时判断当前用户是否存在,可以通过zuul 中 的过滤器实现,具体代码示例如下:@Componentpublic class AuthenticationFilter extends ZuulFilter{ private static final Logger LOG = LoggerFactory.getLogger(AuthenticationFilter.class); @Override public String filterType() {//过滤器类型,pre表示请求被处理之前拦截 return "pre"; } @Override public int filterOrder() {//执行过滤器的顺序号 return 0; } @Override public boolean shouldFilter() {//过滤器是否生效 return true; } @Override public Object run() {//拦截逻辑 RequestContext currentContext = RequestContext.getCurrentContext(); HttpServletRequest request = currentContext.getRequest(); HttpServletResponse response = currentContext.getResponse(); String path = request.getServletPath(); LOG.debug("--------------------"+path+"-----------------------"); if(path.startsWith("/safty-login")) { return null; } HttpSession session = request.getSession(); Object currUser = session.getAttribute(Constants.SESSION_ATTR_CURR_USER); if (currUser == null) { try { response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); out.print("{\"logined\":false}"); out.flush(); out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } throw new RuntimeException();//抛出异常,阻止进一步处理 } return null; }}
转载地址:http://xnhdi.baihongyu.com/