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

从流程可以看出,浏览在访问服务时会携带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 扫描。需要在每个需要用到的微服务模块中添加这个注解。

相关推荐
猿究院_夏燊8 小时前
Spring Bean 生命周期
java·后端·spring
CCblandy9 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
我是人✓9 小时前
SpringBoot项目的五种创建方式
java·spring boot·后端
念念清晰11 小时前
000-OpenJDK17 源码构建:MacOS环境(M芯片)
java·后端
来福猿11 小时前
SpringCloud Config 源码分析:配置拉取、刷新与多环境管理
后端·spring·spring cloud
IT_陈寒13 小时前
React组件莫名重渲染?你可能忘了这个小细节
前端·人工智能·后端
用户98883218717414 小时前
给 .NET 网络调用设定“预算”:用 CancellationTokenSource.CancelAfter 处理超时取消
后端
清风儿32914 小时前
一块不会说话的电表,把我们折腾了半个月
后端
预知同行14 小时前
深入解析 MoE 混合专家模型:从 Router 到 All-to-All 的架构全景与生产实践
后端·架构
upala14 小时前
【仓颉语言入门 · 第二篇 第 8 课】
后端