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

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

相关推荐
xuanWb1 小时前
手写一个 LLM API 网关:Anthropic 与 OpenAI 协议转换的完整实现
后端
苍何2 小时前
给 Codex 换皮肤这门生意,被我开源了
后端
用户8356290780512 小时前
Python 实现 Excel 命名范围(Named Range)的创建与管理
后端·python
程序员David2 小时前
我让 Claude 从架构文档一路干到代码,踩了三个坑才摸清边界
后端
Zane19942 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
神奇小汤圆2 小时前
线程池拒绝策略CallerRunsPolicy反而卡死了主线程
后端
神奇小汤圆2 小时前
Jaws:从零构建一个”五脏俱全”的 Java RPC 框架
后端
Csvn2 小时前
📊 SQL 入门 Day 10:递归 CTE — 破解无限层级查询的终极武器
后端·sql
echohelloworld112 小时前
HarmonyOS开发实战:小分享-CreateSelectPage创建分享类型选择器
后端