最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺析SpringBoot3.x 如何避免內(nèi)部服務(wù)調(diào)用被重復(fù)攔截

 更新時(shí)間:2025年09月28日 09:20:31   作者:網(wǎng)羅開發(fā)  
這篇文章主要為大家詳細(xì)介紹了SpringBoot3.x 避免內(nèi)部服務(wù)調(diào)用被重復(fù)攔截的相關(guān)方法,文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下

前言

在微服務(wù)架構(gòu)里,外部請求和內(nèi)部服務(wù)調(diào)用的認(rèn)證邏輯往往不一樣。

比如外部用戶訪問接口時(shí),必須用 user_token 來校驗(yàn)身份;而服務(wù)之間(比如服務(wù) A 調(diào)用服務(wù) B)只需要用 client_token 來表明這是內(nèi)部調(diào)用,不必再走一遍復(fù)雜的用戶認(rèn)證。

但是問題來了:

用戶帶著 access_token 請求服務(wù) A,A 會先過 Spring Security 的過濾器。這個(gè)過濾器里我們要遠(yuǎn)程調(diào)用服務(wù) B 驗(yàn)證 access_token,結(jié)果 Feign 請求又被 Security 攔了一次,導(dǎo)致認(rèn)證死循環(huán)。

那要怎么破?本文就來聊聊這個(gè)場景的實(shí)戰(zhàn)解法。

需求場景分析

我們先把需求捋清楚:

1.外部請求

  • 必須攜帶 user_token
  • 通過服務(wù) A 時(shí),Spring Security 要走用戶認(rèn)證邏輯

2.服務(wù)間調(diào)用

  • Feign 調(diào)用必須帶 client_token
  • 服務(wù) B 收到請求時(shí),只要驗(yàn)證這個(gè) client_token 就行,不必再走用戶認(rèn)證

3.問題點(diǎn)

當(dāng) A 的 Security 過濾器里用 Feign 調(diào)服務(wù) B 驗(yàn)證 access_token 時(shí),這個(gè)請求也會被攔截,導(dǎo)致無限套娃

常見坑點(diǎn)

  • 沒區(qū)分 token 類型:用戶 token 和 client token 混在一起,所有請求都走同一套攔截邏輯。
  • Feign 默認(rèn)帶全局?jǐn)r截:Spring Security 的 OncePerRequestFilter 會作用于所有請求,包括 Feign 發(fā)起的內(nèi)部請求。
  • 死循環(huán)風(fēng)險(xiǎn):A 調(diào) B 校驗(yàn) token,B 又?jǐn)r截成用戶請求 → 又要去 A 驗(yàn)證 → 死循環(huán)。

所以我們必須設(shè)計(jì)一套機(jī)制,讓外部請求和內(nèi)部調(diào)用走不同的認(rèn)證邏輯。

解決思路

1.區(qū)分兩類請求

  • 用戶請求:帶 Authorization: Bearer user_token
  • 內(nèi)部調(diào)用:帶 X-Client-Token: client_token

2.Spring Security 配置雙認(rèn)證鏈

  • 用戶請求必須走 JWT 校驗(yàn)邏輯
  • 內(nèi)部調(diào)用只校驗(yàn) client_token,繞過用戶認(rèn)證

3.Feign 請求統(tǒng)一加 client_token

RequestInterceptor 給 Feign 請求自動(dòng)加 X-Client-Token

代碼實(shí)戰(zhàn)

下面用 Spring Boot 3.x 寫一個(gè)簡化版 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) -> {
            // 模擬內(nèi)部服務(wù)調(diào)用的 client_token,可以從配置中心或 Vault 獲取
            template.header("X-Client-Token", "my-client-token-123");
        };
    }
}

這樣每次服務(wù) A 調(diào)用服務(wù) B,都會帶上 X-Client-Token。

2. 自定義認(rèn)證過濾器

我們需要兩個(gè)過濾器,一個(gè)處理用戶 token,一個(gè)處理 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)) {
            // 直接認(rèn)證為內(nèi)部服務(wù)角色
            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 并且正確 → 直接放行,視為內(nèi)部請求
  • 沒有的話就交給后面的用戶認(rèn)證邏輯去處理

3. Security 配置雙認(rèn)證邏輯

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 優(yōu)先級要高于 user token filter
        http.addFilterBefore(new ClientTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(new UserTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

這里我們把 ClientTokenFilter 放在前面,這樣內(nèi)部調(diào)用會優(yōu)先匹配,不會再進(jìn)入用戶認(rèn)證邏輯。

4. 用戶 token 認(rèn)證

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: 調(diào)用服務(wù) B 驗(yàn)證 user_token,有效的話設(shè)置用戶上下文
            // 這里省略 JWT 驗(yàn)證邏輯
        }
        filterChain.doFilter(request, response);
    }
}

這樣就能保證:

  • 外部請求 → Authorization: Bearer user_token → 走 UserTokenFilter
  • 內(nèi)部調(diào)用 → X-Client-Token: client_token → 走 ClientTokenFilter

實(shí)際場景結(jié)合

設(shè)想一下:

  • 用戶小明用 app 登錄,請求 /api/orders 帶著 user_token
  • 服務(wù) A 收到請求,要調(diào)用服務(wù) B 校驗(yàn) user_token
  • Feign 調(diào)用時(shí)帶上了 X-Client-Token,所以服務(wù) B 不會再攔截成用戶請求,而是識別成內(nèi)部調(diào)用。
  • 服務(wù) B 驗(yàn)證成功后返回結(jié)果,A 才繼續(xù)執(zhí)行業(yè)務(wù)邏輯。

這樣就避免了死循環(huán),既保證了安全性,又把內(nèi)部調(diào)用和外部請求分開了。

總結(jié)

這個(gè)問題的核心是 區(qū)分外部請求和內(nèi)部調(diào)用的認(rèn)證鏈路

  • 給 Feign 調(diào)用統(tǒng)一加 client_token
  • 在 Security 里加一個(gè) ClientTokenFilter,專門處理內(nèi)部請求
  • 用戶請求繼續(xù)走 JWT 或 access_token 的邏輯
  • 通過過濾器順序,避免 Feign 調(diào)用再觸發(fā)用戶認(rèn)證,徹底解決死循環(huán)

到此這篇關(guān)于淺析SpringBoot3.x 如何避免內(nèi)部服務(wù)調(diào)用被重復(fù)攔截的文章就介紹到這了,更多相關(guān)SpringBoot內(nèi)部服務(wù)調(diào)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

玉环县| 灵武市| 井陉县| 吴桥县| 泽普县| 广汉市| 沾化县| 建昌县| 乌鲁木齐市| 吉安县| 杂多县| 堆龙德庆县| 会理县| 肥东县| 吉木乃县| 渭南市| 云南省| 乐业县| 体育| 武汉市| 琼结县| 九台市| 绍兴县| 天台县| 汝州市| 增城市| 德令哈市| 洞口县| 曲阳县| 安图县| 廉江市| 富蕴县| 厦门市| 镇雄县| 阿巴嘎旗| 亚东县| 克拉玛依市| 武平县| 荔浦县| 榆中县| 黑龙江省|