微服务获取用户信息和OpenFeign传递用户

打印 上一主题 下一主题

主题 807|帖子 807|积分 2421

问题一:
网关已经完成登录校验并获取登录用户身份信息。但是当网关将哀求转发到微服务时,微服务又该如何获取用户身份呢?
由于网关发送哀求到微服务依然采用的是Http哀求,因此我们可以将用户信息以哀求头的方式传递到下游微服务。然后微服务可以从哀求头中获取登录用户信息。思量到微服务内部大概许多地方都必要用到登录用户信息,因此我们可以利用SpringMVC的拦截器来实现登录用户信息获取,并存入ThreadLocal,方便后续利用。
据图流程图如下:
 

因此,接下来我们要做的事情有:
改造网关过滤器,在获取用户信息后生存到哀求头,转发到下游微服务
  1. String userInfo = userId.toString();
  2. exchange.mutate()
  3.         .request(builder -> builder.header("user-info",userInfo)).build();
复制代码
编写微服务拦截器,拦截哀求获取用户信息,生存到ThreadLocal后放行
ThreadLocal相关的类:
  1. package com.hmall.common.utils;
  2. public class UserContext {
  3.     private static final ThreadLocal<Long> tl = new ThreadLocal<>();
  4.     /**
  5.      * 保存当前登录用户信息到ThreadLocal
  6.      * @param userId 用户id
  7.      */
  8.     public static void setUser(Long userId) {
  9.         tl.set(userId);
  10.     }
  11.     /**
  12.      * 获取当前登录用户信息
  13.      * @return 用户id
  14.      */
  15.     public static Long getUser() {
  16.         return tl.get();
  17.     }
  18.     /**
  19.      * 移除当前登录用户信息
  20.      */
  21.     public static void removeUser(){
  22.         tl.remove();
  23.     }
  24. }
复制代码
 拦截器:
  1. package com.hmall.common.interceptor;
  2. import cn.hutool.core.util.StrUtil;
  3. import com.hmall.common.utils.UserContext;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. public class UserInfoInterceptor implements HandlerInterceptor {
  8.     @Override
  9.     public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
  10.         String userId = request.getHeader("User-info");
  11.         if(StrUtil.isNotEmpty(userId)){
  12.             UserContext.setUser(Long.parseLong(userId));
  13.         }
  14.         return true;
  15.     }
  16.     @Override
  17.     public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception {
  18.         UserContext.removeUser();
  19.     }
  20. }
复制代码
配置类:
  1. package com.hmall.common.config;
  2. import com.hmall.common.interceptor.UserInfoInterceptor;
  3. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.DispatcherServlet;
  6. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  8. @Configuration
  9. @ConditionalOnClass(DispatcherServlet.class) //排除网关服务
  10. public class MvcConfig implements WebMvcConfigurer {
  11.     @Override
  12.     public void addInterceptors(InterceptorRegistry registry) {
  13.         registry.addInterceptor(new UserInfoInterceptor()).addPathPatterns("/**");
  14.     }
  15. }
复制代码
基于SpringBoot的主动装配原理,我们要将其添加到resources目次下的META-INF/spring.factories文件中:

 内容如下:
  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2.   com.hmall.common.config.MyBatisConfig,\
  3.   com.hmall.common.config.MvcConfig
复制代码
问题二:
前端发起的哀求都会颠末网关再到微服务,由于我们之前编写的过滤器和拦截器功能,微服务可以轻松获取登录用户信息。
但有些业务是比较复杂的,哀求到达微服务后还必要调用别的多个微服务。比如下单业务,流程如下:

 下单的过程中,必要调用商品服务扣减库存,调用购物车服务清理用户购物车。而清理购物车时必须知道当前登录的用户身份。但是,订单服务调用购物车时并没有传递用户信息,购物车服务无法知道当前用户是谁!
这里要借助Feign中提供的一个拦截器接口:feign.RequestInterceptor

  1. @Bean
  2. public RequestInterceptor userInfoRequestInterceptor(){
  3.     return new RequestInterceptor() {
  4.         @Override
  5.         public void apply(RequestTemplate template) {
  6.             // 获取登录用户
  7.             Long userId = UserContext.getUser();
  8.             if(userId == null) {
  9.                 // 如果为空则直接跳过
  10.                 return;
  11.             }
  12.             // 如果不为空则放入请求头中,传递给下游微服务
  13.             template.header("user-info", userId.toString());
  14.         }
  15.     };
  16. }
复制代码
此外还必要在调用服务的启动类上加上:
  1. @EnableFeignClients(basePackages = "com.hmall.api.client",defaultConfiguration = DefaultFeignConfig.class)
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表