当前位置: 首页 > news >正文

网关登录校验(2)----网关如何将用户信息传递给微服务

1.微服务获取用户信息

现在,网关已经可以完成登录校验并获取登录用户身份信息。但是当网关将请求转发到微服务时,微服务又该如何获取用户身份呢?

由于网关发送请求到微服务依然采用的是Http请求,因此我们可以将用户信息以请求头的方式传递到下游微服务。然后微服务可以从请求头中获取登录用户信息。考虑到微服务内部可能很多地方都需要用到登录用户信息,因此我们可以利用SpringMVC的拦截器来实现登录用户信息获取,并存入ThreadLocal,方便后续使用。

据图流程图如下:

因此,接下来我们要做的事情有:

  • 改造网关过滤器,在获取用户信息后保存到请求头,转发到下游微服务

  • 编写微服务拦截器,拦截请求获取用户信息,保存到ThreadLocal后放行

1.1.保存用户到请求头

首先,我们修改登录校验拦截器的处理逻辑,保存用户信息到请求头中:

package com.hmall.gateway.filters;import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.utils.JwtTool;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final AuthProperties authProperties;private final JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.获取到requestServerHttpRequest request = exchange.getRequest();//2.判断是否需要拦截String path = request.getPath().toString();if(isExcluded(path)){//如果不需要拦截,放行return chain.filter(exchange);}//3.从请求头获取到tokenList<String> headers = request.getHeaders().get("authorization");String token = null;if(headers != null && !headers.isEmpty()){token = headers.get(0);}//4.解析token,获取到用户idLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {//token有问题,拦截ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);//设置401未登录状态码return response.setComplete();//直接结束}//5.TODO 传递用户信息到微服务String userInfo = userId.toString();ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("user-info",userInfo)).build();//6.放行return chain.filter(swe);}private boolean isExcluded(String path) {//匹配,如果成功匹配则truefor (String pathPattern : authProperties.getExcludePaths()) {if (antPathMatcher.match(pathPattern, path)){return true;}}return false;}@Overridepublic int getOrder() {return 0;}
}

1.2.拦截器获取用户信息

在hm-common中已经有一个用于保存登录用户的ThreadLocal工具:

接下来,我们只需要编写拦截器,获取用户信息并保存到UserContext,然后放行即可。

由于每个微服务都有获取登录用户的需求,因此拦截器我们直接写在hm-common中,并写好自动装配。这样微服务只需要引入hm-common就可以直接具备拦截器功能,无需重复编写。

我们在hm-common模块下定义一个拦截器:

package com.hmall.common.interceptor;import cn.hutool.core.util.StrUtil;
import com.hmall.common.utils.UserContext;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//此拦截器不作拦截,而是微服务想获取到网关或者上游微服务传递的请求头(用户信息)//1.获取到请求头(用户信息)String header = request.getHeader("user-info");//2.判断是否为空,有则获取,存入ThreadLocalif(StrUtil.isNotBlank(header)){UserContext.setUser(Long.valueOf(header));}//3.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserContext.removeUser();}
}

接着在hm-common模块下编写SpringMVC的配置类,配置登录拦截器:

package com.hmall.common.config;import com.hmall.common.interceptors.UserInfoInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}

不过,需要注意的是,这个配置类默认是不会生效的,因为它所在的包是com.hmall.common.config,与其它微服务的扫描包不一致,无法被扫描到,因此无法生效。

基于SpringBoot的自动装配原理,我们要将其添加到resources目录下的META-INF/spring.factories文件中:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig

1.3.恢复购物车代码

之前我们无法获取登录用户,所以把购物车服务的登录用户写死了,现在需要恢复到原来的样子。

找到cart-service模块的com.hmall.cart.service.impl.CartServiceImpl

 

 

 

 

 

 

 

 

 

 


http://www.mrgr.cn/news/31335.html

相关文章:

  • Django+React+Neo4j实现的地质领域知识图谱系统
  • DNS解析流程
  • pandas入门
  • day51
  • df将字典转换为df,如何以key为行而不是列
  • 【刷题日记】15. 三数之和
  • 有关JS下隐藏的敏感信息
  • 算法【Dijkstra算法及分层图最短路】
  • C++——用选择法对10个数值进行排序。
  • [嵌入式] 3588相关
  • 码头童话,“丈量”行业数智化转型
  • .config、Kconfig、***_defconfig之间的关系和工作原理
  • 对于C++继承中子类与父类对象同时定义其析构顺序的探究
  • 容器化安装Jenkins部署devops
  • Go Testify学习与使用
  • 预测合理交易目标:中断形态趋势分析
  • 微服务架构陷阱与挑战
  • 4. 密码协议
  • 【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
  • 分布式消息中间件kafka