您现在的位置是:亿华云 > 热点

SpringCloud微服务中网关如何传递用户信息?

亿华云2025-10-02 23:17:00【热点】1人已围观

简介来源:JAVA日知录大家好,我是飘渺。今天来解决一个粉丝的问题:在SpringCloud Gateway实现鉴权时,如何在转发请求到子业务模块时获取登录用户信息?在上一篇文章中,我们已经实现了在网关层

来源:JAVA日知录

大家好,服务我是中网飘渺。

今天来解决一个粉丝的关何问题:在SpringCloud Gateway实现鉴权时,如何在转发请求到子业务模块时获取登录用户信息?传递

在上一篇文章中,我们已经实现了在网关层进行身份认证:首先从请求头中获取 Authorization 参数得到 JWT Token,用户然后解开 JWT 后即可获取用户信息。信息

以下是服务相关代码:

@Overridepublic AuthenticatorResult auth(ServerWebExchange exchange) 

{

 ServerHttpRequest request = exchange.getRequest();

 HttpHeaders httpHeaders = request.getHeaders();

 // 获取JWT请求头 Authorization

 String token = httpHeaders.getFirst(HttpHeaders.AUTHORIZATION);

 if

 (Objects.nonNull(token)) {

  try

 {

   String subjectFromJWT = JwtUtil.getSubjectFromJWT(token);

   log.info("用户请求token: { } , 身份Subject:{ }"

, token, subjectFromJWT);

   return new AuthenticatorResult(true, "认证通过"

);

  } catch

 (ParseException | JOSEException e) {

   log.error("token解析失败{ }"

,token);

   return new AuthenticatorResult(false, "Token错误,请重新登录!"

);

  }

 }

 return new AuthenticatorResult(false, "Token为空,请重新登录!"

);

}

实现方案

通常情况下,在 Spring Cloud 中将用户信息透传给后端服务有两种方式:

第一种:在网关解析出用户的中网 Token 得到用户 ID,然后将用户 ID 添加到请求头中传递下去。关何

第二种:在网关直接把 Token 传递下去,传递由各个子服务自行解析。用户

在 DailyMart 中我推荐使用第一种方式,信息将 JWT Token 解析后直接透传给后端服务。服务

以下是中网实现方案:

在网关解析 JWT Token 后得到 UserID,修改 Spring Cloud Gateway 的关何请求头,将 UserID 添加到请求头中。自定义用户上下文 UserContextHolder,服务器托管并使用 ThreadLocal 进行存储。在微服务中的 Web 组件中创建拦截器 UserTokenInterceptor,从 Request 中获取 UserID,并将其添加到用户上下文 UserContextHolder 中。将拦截器 UserInterceptor 注册到 Spring 容器中。

代码实现

1、在SpringCloud Gateway中修改请求头

public class DefaultApiAuthenticator implements ApiAuthenticator 

{

    @Override    public AuthenticatorResult auth(ServerWebExchange exchange) 

{

       ...

        if

 (Objects.nonNull(token)) {

            try

 {

                String subjectFromJWT = JwtUtil.getSubjectFromJWT(token);

              //重新设置请求头

                mutateNewHeader(exchange, subjectFromJWT);

                return new AuthenticatorResult(true, "认证通过"

);

            } catch

 (ParseException | JOSEException e) {

                log.error("token解析失败"

);

                return new AuthenticatorResult(false, "Token错误,请重新登录!"

);

            }          

        }        

        return new AuthenticatorResult(false, "Token为空,请重新登录!"

);

    }

    

/

**

     * 重新构建请求头,将用户账号放入Token

     * @param

 subject 用户身份

     */     private void mutateNewHeader(ServerWebExchange exchange, String subject) 

{

        ServerHttpRequest newRequest = exchange.getRequest().mutate().header(CommonConstant.X_CLIENT_TOKEN, subject).build();

        exchange.mutate().request(newRequest).build();

    }

}

2、自定义用户上下文UserContextHolder

public class UserContextHolder 

{

    private final

 ThreadLocal threadLocal;

    private UserContextHolder() 

{

        this.threadLocal = new

 ThreadLocal<>();

    }

    public static UserContextHolder getInstance() 

{

        return

 SingletonHolder.instance;

    }

    

/

**

     * 设置用户数据

     * @param

 userId 用户账号

     */     public void setCurrentUser(String userId) 

{

        this

.threadLocal.set(userId);

    }

    

/

**

     * 获取当前用户

     * @return

 userId

     */     public String getCurrentUser() 

{

        return this

.threadLocal.get();

    }

    

/

**

     * 清理用户信息

     */     public void clear() 

{

        this

.threadLocal.remove();

    }

}

3、创建自定义拦截器UserTokenInterceptor

由于每个服务中都需要用到此功能,所以我们将此功能在公共组件dailymart-web-spring-boot-starter中实现。

public class UserTokenInterceptor implements HandlerInterceptor 

{

    @Override    public boolean preHandle(HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception 

{

        String userId = request.getHeader(CommonConstant.X_CLIENT_TOKEN);

        // 设置用户信息

        UserContextHolder.getInstance().setCurrentUser(userId);

        return true

;

    }

    @Override    public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) throws Exception 

{

        UserContextHolder.getInstance().clear();

    }

}

这里先从请求头中获取userId,然后使用单例类UserContextHolder将userId添加到ThreadLocal中,当然在处理完成后需要清空ThreadLocal的值,不然会出现内存泄露。

4、创建配置类,在Spring中注册拦截器

@SpringBootConfiguration@ConditionalOnWebApplicationpublic class WebMvcConfigurerAdaptor implements WebMvcConfigurer 

{

    @Override    public void addInterceptors(InterceptorRegistry registry) 

{

        registry.addInterceptor(userInterceptor())

                .addPathPatterns("/api/pd/**"

)

                .excludePathPatterns(excludePathList);

    }

    private final String[] excludePathList = new

 String[]{

            "/api/pd/customer/login"

    };

    @Bean    public HandlerInterceptor userInterceptor() 

{

        return new

 UserTokenInterceptor();

    }

}

在上篇文章中我们已经规定来源标识为/pd的才算是浏览器的请求,源码库所以在这个拦截器中我们只需要配置特定的拦截地址即可。

5、获取userId

在后续微服务中如果需要获取 UserID,只需从 UserContextHolder 获取即可:

@Operation(summary = "用户测试接口"

)

@PostMapping("/api/pd/customer/info"

)

public void info() 

{

 String currentUser = UserContextHolder.getInstance().getCurrentUser();

 log.info("当前登录用户:"

 + currentUser);

}

小结

本文介绍了在 Spring Cloud Gateway 中实现鉴权并将用户信息传递给后端服务的解决方案。通过解析 JWT Token 并将用户身份信息添加到请求头,实现了简单而有效的用户身份认证和信息传递。当然,文章中仅仅传递了UserID,在实际使用中,大家也可以先在网关层通过UserID获取用户的详细信息,再将详细信息进行传递。同时,本文涉及的代码都已经上传至Github,感兴趣的可以通过文末方式获取。

很赞哦!(62)