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

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

相关推荐
噢,我明白了2 小时前
java中唯一键和幂等键的应用
java·后端
GreenTea2 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
考虑考虑2 小时前
synchronized字符串常量
java·后端·java ee
颜进强3 小时前
16 · NestJS LifecycleEvents 生命周期事件:五个钩子、三条边界,和 rag-server 的优雅停机
前端·后端·ai编程
GetcharZp4 小时前
定位不再一个个吐坐标:英伟达 LocateAnything 上手全攻略
后端
郑州光合科技余经理5 小时前
同城电商系统:库存变更怎么同步到订单
java·开发语言·前端·后端·uni-app·php·ai编程
我叫黑大帅5 小时前
Go日志库工程选型与逃逸分析评测报告
后端·面试·go
IT_陈寒7 小时前
JavaScript闭包的这个坑,我居然今天才爬出来
前端·人工智能·后端
Thneonl7 小时前
消息队列选型决策树:RabbitMQ vs Kafka vs Redis Streams
后端·架构