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

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

相关推荐
IT_陈寒1 小时前
Python的GIL让我多写了500行代码
前端·人工智能·后端
IT爱学堂2 小时前
精讲软件设计师(软考中级)一站式通关课_实战课程_慕课网
后端
战天战地站房顶3 小时前
在 Windows Docker 中部署 PostgreSQL 17 + pgvector 完整指南
后端
卷无止境3 小时前
FastAPI Guard 全解析,从概念到工程落地的实战指南
后端·python
卷无止境3 小时前
FastAPI Users 全面解析:概念、原理与工程实战
后端·python
程序员爱钓鱼3 小时前
Go switch 详解
后端·面试·go
程序员爱钓鱼3 小时前
Rust Struct结构体详解:定义自己的复杂数据类型
后端·面试·rust
风流 少年3 小时前
Spring AI 2.0:MCP
java·后端·spring
北斗落凡尘4 小时前
LangGraph 入门实战(7)
后端·langchain
uzong4 小时前
业务新老系统数据迁移-负责人经验总结和复盘
后端