目录
1 网关获取用户校验信息并保存至请求头(前端-网关)
2 微服务获取网关中的用户校验信息(网关-微服务)
2.1 一般的做法是在公共的module中添加,此处示例为common 公共配置module中添加
2.2 定义拦截器
2.3 定义注册类
2.4 手动添加注册配置类保证被扫描
3 多个微服务之间共享该信息(微服务-微服务)
3.1 定义请求拦截器RequestInterceptor
4 前端-网关-微服务-微服务间信息共享全流程
4.1 (前端-网关)首先前端发起请求,经过GlobalFilter过滤器判断是否需要登录,需要登录的话进行登录校验,完成登录校验后获取到用户信息,存入上下文中,保存到请求头中。然后由网关传向微服务中。
4.2 (网关-微服务)微服务拦截器拦截从网关发送过来的请求,从请求头中获取到用户信息后会通过ThreadLocal保存使用。
4.3 (微服务-微服务)微服务若再向其他微服务发起请求,则通过OpenFeign提供的RequestInterceptor拦截发出的请求,并进行将用户信息保存在请求头中的操作。
1 网关获取用户校验信息并保存至请求头(前端-网关)
思路是添加过滤器,网关过滤器有两种,分别是:
GatewayFilter:路由过滤器,作用于任意指定的路由;默认不生效,要配置到路由后生效。
GlobalFilter:全局过滤器,作用范围是所有路由;声明后自动生效。
两种过滤器的过滤方法签名完全一致。
此处以GlobalFilter为例实现网关登录校验
首先定义定义全局过滤器并实现两个接口,特别说明Ordered接口是改变其权重,提升其优先级别,保证在pre阶段先执行(若不懂pre阶段请看3 网关处理流程)。过滤器定义逻辑如下,拦截符合条件的请求(常见的是需要登录的请求)-> 登录校验 -> 将信息存入上下文请求头中
@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final AuthProperties authProperties; //配置文件类,开发无需登录路径
private final JwtTool jwtTool; //jwt工具类
private final AntPathMatcher antPathMatcher = new AntPathMatcher(); //路径匹配
//工具类
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 其中exchange 为上下文,在过滤器链路中负责传递信息
// 1.获取request 请求
ServerHttpRequest request = exchange.getRequest();
// 2.判断是否需要做登陆拦截
if(isExclude(request.getPath().toString())){
// 放行
return chain.filter(exchange);
}
// 3.获取token
HttpHeaders headers = request.getHeaders();
String token = null;
List<String> authorization = headers.get("authorization");
if(authorization !=null && !authorization.isEmpty()){
token = authorization.get(0);
}
// 4.校验并解析token
Long userId = null;
try {
userId = jwtTool.parseToken(token);
}catch (UnauthorizedException e){
// 拦截
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// TODO 5.传递用户信息
String userInfo= userId.toString();
//存入上下文
ServerWebExchange swe = exchange.mutate()
.request(builder -> builder.header("user-info", userInfo))
.build();
// 6.放行 传入下一层过滤器中
return chain.filter(swe);
}
//工具方法,用来判断是否包含在公共开放的路径中
private boolean isExclude(String path) {
for (String pathPattern: authProperties.getExcludePaths()) {
if (antPathMatcher.match(pathPattern,path)){
return true;
}
}
return false;
}
// 设置在过滤器中的级别,数字越小,级别越高,在pre过程中先执行
@Override
public int getOrder() {
return 0;
}
}
关建步骤在5,通过5将用户信息存入上下文中,然后放在请求头中。即可完成将获取用户校验信息并存入请求头中。
2 微服务获取网关中的用户校验信息(网关-微服务)
获取方法是加拦截器,加在每一个微服务中,这样就会拦截从网关路由过来的请求,然后获取请求头中的用户信息,最后放在ThreadLocal中即可共享信息。
2.1 一般的做法是在公共的module中添加,此处示例为common 公共配置module中添加
2.2 定义拦截器
public class UserInfoInterceptor implements HandlerInterceptor {
//重写方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.获取登陆用户信息
String userInfo = request.getHeader("user-info");
// 2.判断是否获取了用户 如果有 直接存入ThreadLocal
if(StrUtil.isNotBlank(userInfo)){
UserContext.setUser(Long.valueOf(userInfo));
}
// 3.放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 业务逻辑结束清理用户信息 移除ThreadLocal中的用户信息
UserContext.removeUser();
return ;
}
}
拦截器定义以后需要注册到spring Mvc。
2.3 定义注册类
(关于添加下面第二个注解的原因,如果不添加,默认在所有微服务中都生效,那么在启动微服务的时候,所以会自动扫描微服务中的Mvc基础配置,但在网关gateway微服务中并不是SpringMvc,因此会扫描不到而出错。添加的注解是有SpringMvc核心容器类的时候才会生效,这样就只有是SpringMvc的微服务才会生效,避免错误。)
@Configuration
@ConditionalOnClass(DispatcherServlet.class)// 添加此注解是避免在网关微服务中因扫描不到Mvc配置而出错
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry 是注册类,调用下面的方法,添加拦截器
registry.addInterceptor(new UserInfoInterceptor());
}
}
像这种添加在公共包下的配置类因为不是一个spring项目,没有启动类,因此spring 默认扫描不到这个MvcConfig配置类,因此像这种公共包下的配置需要在配置文件中手动添加,如下将配置类路径添加到文件中即可被扫描到
2.4 手动添加注册配置类保证被扫描
以上情况就解决了从网关到各个微服务之间携带用户信息的问题,思路是微服务拦截网关到达的请求并获取用户信息。
3 多个微服务之间共享该信息(微服务-微服务)
思路也是添加拦截器,注意此拦截器是在加在发起请求的微服务中,拦截其发起的请求,然后将用户信息存入请求头中。因为我们使用OpenFeign,它提供了一个拦截器接口,会拦截所以请求,可以向请求头中添加信息,因此直接和Feign接口定义在一个包下。
3.1 定义请求拦截器RequestInterceptor
以下拦截器即可完成拦截发出的请求并将共享信息保存在请求头中的任务
//此处采用匿名内部类的方法,直接定义在了之前的配置类中,也可单独创建一个类
public class DefaultFeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
//用来处理用户信息
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
// 获取用户信息,这是之前网关向微服务传递信息时存入了Thredlocal中的用户信息,取出
Long userId = UserContext.getUser();
if (userId!=null){
// 将获取到的用户信息存入请求头中
requestTemplate.header("user-info",userId.toString());
}
}
};
}
}