微服务框架中获取用户信息

从流程可以看出,浏览在访问服务时会携带jwt向微服务发起请求,中间会先经过网关。网关从jwt中获取用户信息并保存到请求中,然后再转发给对应服务。在到达业务层之前会有拦截器将用户信息存入ThreadLocal中。

1.在经过网关过滤器被拦截时将用户信息保存到请求头中

ini 复制代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  
    // 获取request  
    ServerHttpRequest request = exchange.getRequest();  
    // 判断是否需要拦截  
    if(isExclude(request.getPath().toString())){  
        // 无需拦截 放行  
        return chain.filter(exchange);  
    }  
    // 获取请求头中的token  
    String token = null;  
    List<String> headers = request.getHeaders().get("authorization");  
    if(CollUtils.isNotEmpty(headers)){  
        token = headers.get(0);  
    }  
    //校验并解析token  
    Long userId = null;  
    try {  
        userId = jwtTool.parseToken(token);  
    } catch (UnauthorizedException e) {  
        //无效 拦截  
        ServerHttpResponse response = exchange.getResponse();  
        response.setRawStatusCode(401);  
        return response.setComplete();  
    }  
    // 如果有效,传递用户信息  
    String userInfo = userId.toString();  
    ServerWebExchange ex = exchange.mutate()  
            .request(b -> b.header("user-info", userInfo))  
            .build();  
    // 放行  
    return chain.filter(exchange);  
}

由于jwt令牌中会携带用户id,将用户id存入请求头 "user-info" 中;

2.在公用模块中定义拦截器,在拦截器中将请求头中用户信息保存到ThreadLocal中

java 复制代码
public class UserInfoInterceptor implements HandlerInterceptor {  
  
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        String userInfo = request.getHeader("user-info");  
        //保存到ThreadLocal  
        if(StrUtil.isNotBlank(userInfo)){  
            UserContext.setUser(Long.valueOf(userInfo));  
        }  
        //放行  
        return true;  
    }  
  
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  
        //移除用户  
        UserContext.removeUser();  
    }  
}

"user-info" 请求头中获取到用户id,将其保存到ThreadLocal中。在经过网关后会先经过该拦截器再到达业务层,从而携带登录用户的id到业务逻辑中。

less 复制代码
@Configuration  
@ConditionalOnClass(DispatcherServlet.class)  
public class MvcConfig implements WebMvcConfigurer {  
  
    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(new UserInfoInterceptor());  
    }  
}

在该模块中定义SpringMVC的配置类,将定义的拦截器注册

由于是在微服务框架中,在其他启动模块中不会扫描到MvcConfig,所以需要手动配置

3.处理跨服务调用无法拿到用户信息的问题

当基于OpenFeign实现跨服务调用时,从一个服务向另一个服务发送请求时Feign会重新创建一个Http请求,而这个新的请求并不会继承原始的请求头信息。所以需要实现Feign提供的一个拦截器接口:feign.RequestInterceptor

typescript 复制代码
public class DefaultFeignConfig {  

    @Bean  
    public RequestInterceptor userInfoRequestInterceptor(){  
        return new RequestInterceptor(){  
            @Override  
            public void apply(RequestTemplate requestTemplate) {  
                //获取登录用户  
                Long userId = UserContext.getUser();  
                if(userId == null){  
                    return;  
                }  
                //如果不为空,添加到请求头中  
                requestTemplate.header("user-info", userId.toString());  
            }  
        };  
    }  
}

在Feign发送Http请求前,通过RequestInterceptor拦截器将请求头添加到Feign创建的请求的头信息中。

Pasted image 20260724174630.png由于这是Feign的最佳实践,如果在DefaultFeignConfig类中加了 @Configuration,Spring 组件扫描会把它扫进主容器,里面的 @Bean 就变成全局 Bean 了。而 Feign 的配置类应该只作用于 Feign 自身的上下文,避免污染主容器。所以 Feign 框架会自己拿到这个类,为它创建独立的子上下文,完全不需要 Spring 扫描。需要在每个需要用到的微服务模块中添加这个注解。从流程可以看出,浏览在访问服务时会携带jwt向微服务发起请求,中间会先经过网关。网关从jwt中获取用户信息并保存到请求中,然后再转发给对应服务。在到达业务层之前会有拦截器将用户信息存入ThreadLocal中。

1.在经过网关过滤器被拦截时将用户信息保存到请求头中

ini 复制代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  
    // 获取request  
    ServerHttpRequest request = exchange.getRequest();  
    // 判断是否需要拦截  
    if(isExclude(request.getPath().toString())){  
        // 无需拦截 放行  
        return chain.filter(exchange);  
    }  
    // 获取请求头中的token  
    String token = null;  
    List<String> headers = request.getHeaders().get("authorization");  
    if(CollUtils.isNotEmpty(headers)){  
        token = headers.get(0);  
    }  
    //校验并解析token  
    Long userId = null;  
    try {  
        userId = jwtTool.parseToken(token);  
    } catch (UnauthorizedException e) {  
        //无效 拦截  
        ServerHttpResponse response = exchange.getResponse();  
        response.setRawStatusCode(401);  
        return response.setComplete();  
    }  
    // 如果有效,传递用户信息  
    String userInfo = userId.toString();  
    ServerWebExchange ex = exchange.mutate()  
            .request(b -> b.header("user-info", userInfo))  
            .build();  
    // 放行  
    return chain.filter(exchange);  
}

由于jwt令牌中会携带用户id,将用户id存入请求头* "user-info" *中;

2.在公用模块中定义拦截器,在拦截器中将请求头中用户信息保存到ThreadLocal中

java 复制代码
public class UserInfoInterceptor implements HandlerInterceptor {  
  
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        String userInfo = request.getHeader("user-info");  
        //保存到ThreadLocal  
        if(StrUtil.isNotBlank(userInfo)){  
            UserContext.setUser(Long.valueOf(userInfo));  
        }  
        //放行  
        return true;  
    }  
  
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  
        //移除用户  
        UserContext.removeUser();  
    }  
}

从* "user-info" *请求头中获取到用户id,将其保存到ThreadLocal中。在经过网关后会先经过该拦截器再到达业务层,从而携带登录用户的id到业务逻辑中。

less 复制代码
@Configuration  
@ConditionalOnClass(DispatcherServlet.class)  
public class MvcConfig implements WebMvcConfigurer {  
  
    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(new UserInfoInterceptor());  
    }  
}

在该模块中定义SpringMVC的配置类,将定义的拦截器注册

697由于是在微服务框架中,在其他启动模块中不会扫描到MvcConfig,所以需要手动配置

3.处理跨服务调用无法拿到用户信息的问题

当基于OpenFeign实现跨服务调用时,从一个服务向另一个服务发送请求时Feign会重新创建一个Http请求,而这个新的请求并不会继承原始的请求头信息。所以需要实现Feign提供的一个拦截器接口:feign.RequestInterceptor

typescript 复制代码
public class DefaultFeignConfig {  

    @Bean  
    public RequestInterceptor userInfoRequestInterceptor(){  
        return new RequestInterceptor(){  
            @Override  
            public void apply(RequestTemplate requestTemplate) {  
                //获取登录用户  
                Long userId = UserContext.getUser();  
                if(userId == null){  
                    return;  
                }  
                //如果不为空,添加到请求头中  
                requestTemplate.header("user-info", userId.toString());  
            }  
        };  
    }  
}

在Feign发送Http请求前,通过RequestInterceptor拦截器将请求头添加到Feign创建的请求的头信息中。

Pasted image 20260724174630.png由于这是Feign的最佳实践,如果在DefaultFeignConfig类中加了 @Configuration,Spring 组件扫描会把它扫进主容器,里面的 @Bean 就变成全局 Bean 了。而 Feign 的配置类应该只作用于 Feign 自身的上下文,避免污染主容器。所以 Feign 框架会自己拿到这个类,为它创建独立的子上下文,完全不需要 Spring 扫描。需要在每个需要用到的微服务模块中添加这个注解。从流程可以看出,浏览在访问服务时会携带jwt向微服务发起请求,中间会先经过网关。网关从jwt中获取用户信息并保存到请求中,然后再转发给对应服务。在到达业务层之前会有拦截器将用户信息存入ThreadLocal中。

1.在经过网关过滤器被拦截时将用户信息保存到请求头中

ini 复制代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  
    // 获取request  
    ServerHttpRequest request = exchange.getRequest();  
    // 判断是否需要拦截  
    if(isExclude(request.getPath().toString())){  
        // 无需拦截 放行  
        return chain.filter(exchange);  
    }  
    // 获取请求头中的token  
    String token = null;  
    List<String> headers = request.getHeaders().get("authorization");  
    if(CollUtils.isNotEmpty(headers)){  
        token = headers.get(0);  
    }  
    //校验并解析token  
    Long userId = null;  
    try {  
        userId = jwtTool.parseToken(token);  
    } catch (UnauthorizedException e) {  
        //无效 拦截  
        ServerHttpResponse response = exchange.getResponse();  
        response.setRawStatusCode(401);  
        return response.setComplete();  
    }  
    // 如果有效,传递用户信息  
    String userInfo = userId.toString();  
    ServerWebExchange ex = exchange.mutate()  
            .request(b -> b.header("user-info", userInfo))  
            .build();  
    // 放行  
    return chain.filter(exchange);  
}

由于jwt令牌中会携带用户id,将用户id存入请求头* "user-info" *中;

2.在公用模块中定义拦截器,在拦截器中将请求头中用户信息保存到ThreadLocal中

java 复制代码
public class UserInfoInterceptor implements HandlerInterceptor {  
  
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        String userInfo = request.getHeader("user-info");  
        //保存到ThreadLocal  
        if(StrUtil.isNotBlank(userInfo)){  
            UserContext.setUser(Long.valueOf(userInfo));  
        }  
        //放行  
        return true;  
    }  
  
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  
        //移除用户  
        UserContext.removeUser();  
    }  
}

从* "user-info" *请求头中获取到用户id,将其保存到ThreadLocal中。在经过网关后会先经过该拦截器再到达业务层,从而携带登录用户的id到业务逻辑中。

less 复制代码
@Configuration  
@ConditionalOnClass(DispatcherServlet.class)  
public class MvcConfig implements WebMvcConfigurer {  
  
    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(new UserInfoInterceptor());  
    }  
}

在该模块中定义SpringMVC的配置类,将定义的拦截器注册

697由于是在微服务框架中,在其他启动模块中不会扫描到MvcConfig,所以需要手动配置

3.处理跨服务调用无法拿到用户信息的问题

当基于OpenFeign实现跨服务调用时,从一个服务向另一个服务发送请求时Feign会重新创建一个Http请求,而这个新的请求并不会继承原始的请求头信息。所以需要实现Feign提供的一个拦截器接口:feign.RequestInterceptor

typescript 复制代码
public class DefaultFeignConfig {  

    @Bean  
    public RequestInterceptor userInfoRequestInterceptor(){  
        return new RequestInterceptor(){  
            @Override  
            public void apply(RequestTemplate requestTemplate) {  
                //获取登录用户  
                Long userId = UserContext.getUser();  
                if(userId == null){  
                    return;  
                }  
                //如果不为空,添加到请求头中  
                requestTemplate.header("user-info", userId.toString());  
            }  
        };  
    }  
}

在Feign发送Http请求前,通过RequestInterceptor拦截器将请求头添加到Feign创建的请求的头信息中。

由于这是Feign的最佳实践,如果在DefaultFeignConfig类中加了 @Configuration,Spring 组件扫描会把它扫进主容器,里面的 @Bean 就变成全局 Bean 了。而 Feign 的配置类应该只作用于 Feign 自身的上下文,避免污染主容器。所以 Feign 框架会自己拿到这个类,为它创建独立的子上下文,完全不需要 Spring 扫描。需要在每个需要用到的微服务模块中添加这个注解。

相关推荐
newerp22 分钟前
Golang 调度循环:Go runtime 如何永不停歇地找人干活
后端·程序员·go
明月_清风40 分钟前
AI 时代,为什么架构师又开始谈"本体论"?
人工智能·后端·agent
行百里er2 小时前
BeanPostProcessor:包装 Feign Client
spring boot·后端·监控
花生了什么事o2 小时前
自定义Spring Boot Starter全流程
java·spring boot·后端
云上小朱3 小时前
开发测试环境-Kubernetes离线部署指南
后端
遨翔在知识的海洋里3 小时前
nestjs(1)-模块的相互调用
后端
Profile排查笔记3 小时前
指纹浏览器推荐:用一套验收清单筛选 Profile、代理与自动化能力
前端·人工智能·后端·自动化
jsl_jsl_jsl3 小时前
JUC速记
后端
站大爷IP3 小时前
Python的pip依赖把我折腾惨了,原来requirements.txt和poetry能打出火星撞地球
后端