浅析SpringBoot3.x 如何避免内部服务调用被重复拦截

 更新时间:2025年09月28日 09:20:31   作者:网罗开发  
这篇文章主要为大家详细介绍了SpringBoot3.x 避免内部服务调用被重复拦截的相关方法,文中的示例代码讲解详细,有需要的小伙伴可以了解下

前言

在微服务架构里,外部请求和内部服务调用的认证逻辑往往不一样

比如外部用户访问接口时,必须用 user_token 来校验身份;而服务之间(比如服务 A 调用服务 B)只需要用 client_token 来表明这是内部调用,不必再走一遍复杂的用户认证。

但是问题来了:

用户带着 access_token 请求服务 A,A 会先过 Spring Security 的过滤器。这个过滤器里我们要远程调用服务 B 验证 access_token,结果 Feign 请求又被 Security 拦了一次,导致认证死循环。

那要怎么破?本文就来聊聊这个场景的实战解法。

需求场景分析

我们先把需求捋清楚:

1.外部请求

  • 必须携带 user_token
  • 通过服务 A 时,Spring Security 要走用户认证逻辑

2.服务间调用

  • Feign 调用必须带 client_token
  • 服务 B 收到请求时,只要验证这个 client_token 就行,不必再走用户认证

3.问题点

当 A 的 Security 过滤器里用 Feign 调服务 B 验证 access_token 时,这个请求也会被拦截,导致无限套娃

常见坑点

  • 没区分 token 类型:用户 token 和 client token 混在一起,所有请求都走同一套拦截逻辑。
  • Feign 默认带全局拦截:Spring Security 的 OncePerRequestFilter 会作用于所有请求,包括 Feign 发起的内部请求。
  • 死循环风险:A 调 B 校验 token,B 又拦截成用户请求 → 又要去 A 验证 → 死循环。

所以我们必须设计一套机制,让外部请求和内部调用走不同的认证逻辑

解决思路

1.区分两类请求

  • 用户请求:带 Authorization: Bearer user_token
  • 内部调用:带 X-Client-Token: client_token

2.Spring Security 配置双认证链

  • 用户请求必须走 JWT 校验逻辑
  • 内部调用只校验 client_token,绕过用户认证

3.Feign 请求统一加 client_token

RequestInterceptor 给 Feign 请求自动加 X-Client-Token

代码实战

下面用 Spring Boot 3.x 写一个简化版 Demo,演示完整流程。

1. Feign 请求加上 client_token

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor clientTokenInterceptor() {
        return (RequestTemplate template) -> {
            // 模拟内部服务调用的 client_token,可以从配置中心或 Vault 获取
            template.header("X-Client-Token", "my-client-token-123");
        };
    }
}

这样每次服务 A 调用服务 B,都会带上 X-Client-Token

2. 自定义认证过滤器

我们需要两个过滤器,一个处理用户 token,一个处理 client token。

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class ClientTokenFilter extends OncePerRequestFilter {

    private final String CLIENT_TOKEN = "my-client-token-123";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        String clientToken = request.getHeader("X-Client-Token");
        if (clientToken != null && clientToken.equals(CLIENT_TOKEN)) {
            // 直接认证为内部服务角色
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken("internal-service", null, null);
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }
}

这里的逻辑很简单:

  • 如果请求头有 X-Client-Token 并且正确 → 直接放行,视为内部请求
  • 没有的话就交给后面的用户认证逻辑去处理

3. Security 配置双认证逻辑

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable());

        http.authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
        );

        // client token filter 优先级要高于 user token filter
        http.addFilterBefore(new ClientTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(new UserTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

这里我们把 ClientTokenFilter 放在前面,这样内部调用会优先匹配,不会再进入用户认证逻辑。

4. 用户 token 认证

public class UserTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String userToken = authHeader.substring(7);
            // TODO: 调用服务 B 验证 user_token,有效的话设置用户上下文
            // 这里省略 JWT 验证逻辑
        }
        filterChain.doFilter(request, response);
    }
}

这样就能保证:

  • 外部请求 → Authorization: Bearer user_token → 走 UserTokenFilter
  • 内部调用 → X-Client-Token: client_token → 走 ClientTokenFilter

实际场景结合

设想一下:

  • 用户小明用 app 登录,请求 /api/orders 带着 user_token
  • 服务 A 收到请求,要调用服务 B 校验 user_token
  • Feign 调用时带上了 X-Client-Token,所以服务 B 不会再拦截成用户请求,而是识别成内部调用。
  • 服务 B 验证成功后返回结果,A 才继续执行业务逻辑。

这样就避免了死循环,既保证了安全性,又把内部调用和外部请求分开了。

总结

这个问题的核心是 区分外部请求和内部调用的认证链路

  • 给 Feign 调用统一加 client_token
  • 在 Security 里加一个 ClientTokenFilter,专门处理内部请求
  • 用户请求继续走 JWT 或 access_token 的逻辑
  • 通过过滤器顺序,避免 Feign 调用再触发用户认证,彻底解决死循环

到此这篇关于浅析SpringBoot3.x 如何避免内部服务调用被重复拦截的文章就介绍到这了,更多相关SpringBoot内部服务调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring boot集成redis lettuce代码实例

    Spring boot集成redis lettuce代码实例

    这篇文章主要介绍了Spring boot集成redis lettuce代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 详解MyBatis的XML实现方法(附带注解方式实现)

    详解MyBatis的XML实现方法(附带注解方式实现)

    这篇文章主要详细介绍了MyBatis的XML实现方法(附带注解方式实现),文中通过代码示例给大家讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-05-05
  • Java用POI解析excel并获取所有单元格数据的实例

    Java用POI解析excel并获取所有单元格数据的实例

    下面小编就为大家带来一篇Java用POI解析excel并获取所有单元格数据的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • SpringBoot实现多数据源的实战案例

    SpringBoot实现多数据源的实战案例

    这篇文章主要介绍了SpringBoot实现多数据源的实战案例,文中通过示例代码和图文展示介绍的非常详细,对大家的学习或工作有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2024-01-01
  • Java编程调用微信支付功能的方法详解

    Java编程调用微信支付功能的方法详解

    这篇文章主要介绍了Java编程调用微信支付功能的方法,结合实例形式详细分析了java微信支付功能的原理、操作流程及相关实现技巧,需要的朋友可以参考下
    2017-08-08
  • SpringBoot四大神器之Auto onfiguration的使用

    SpringBoot四大神器之Auto onfiguration的使用

    本文主要介绍了SpringBoot四大神器之Auto Configuration,springboot auto configuration的本质就是自动配置spring的各种bean。感兴趣的可以了解一下
    2021-10-10
  • jdbc链接远程数据库进行修改url操作

    jdbc链接远程数据库进行修改url操作

    这篇文章主要为大家详细介绍了jdbc链接远程数据库进行修改url操作,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • 如何解决异步线程导致的traceId为空的问题

    如何解决异步线程导致的traceId为空的问题

    文章讨论了在使用异步线程时,traceId为空的问题,并提出了使用线程池的解决方案,作者分享了个人经验,并鼓励大家参考和支持脚本之家
    2025-02-02
  • springboot jpa分库分表项目实现过程详解

    springboot jpa分库分表项目实现过程详解

    这篇文章主要介绍了springboot jpa分库分表项目实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Maven发布项目到Nexus私有服务器

    Maven发布项目到Nexus私有服务器

    本文主要介绍了Maven发布项目到Nexus私有服务器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07

最新评论