Spring Boot 3.x + Security + OpenFeign:如何避免内部服务调用被重复拦截? - 详解

news/2025/9/28 9:21:04/文章来源:https://www.cnblogs.com/tlnshuju/p/19116115

Spring Boot 3.x + Security + OpenFeign:如何避免内部服务调用被重复拦截? - 详解

2025-09-28 09:17  tlnshuju  阅读(0)  评论(0)    收藏  举报

网罗开发(小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
大家好,我是展菲!
全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


文章目录

    • 前言
    • 需求场景分析
    • 常见坑点
    • 解决思路
    • 代码实战
      • 1. Feign 请求加上 client_token
      • 2. 自定义认证过滤器
      • 3. Security 配置双认证逻辑
      • 4. 用户 token 认证
    • 实际场景结合
    • 总结

前言

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

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

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

需求场景分析

我们先把需求捋清楚:

  1. 外部请求

  2. 服务间调用

  3. 问题点

常见坑点

  • 没区分 token 类型
    用户 token 和 client token 混在一起,所有请求都走同一套拦截逻辑。

  • Feign 默认带全局拦截
    Spring Security 的 OncePerRequestFilter 会作用于所有请求,包括 Feign 发起的内部请求。

  • 死循环风险
    A 调 B 校验 token,B 又拦截成用户请求 → 又要去 A 验证 → 死循环。

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

解决思路

  1. 区分两类请求

  2. Spring Security 配置双认证链

  3. Feign 请求统一加 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 才继续执行业务逻辑。

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

总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/920442.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

完整的GLFW应用程序示例

/** 完整的GLFW应用程序示例* 包含窗口创建、输入处理、渲染循环等所有主要功能* 每行代码都有详细注释,便于学习GLFW的使用*/#include <GLFW/glfw3.h> // GLFW主头文件 #include <iostream> // 用…

网站规划建设实训大型电商网站开发方案

1. 引言 Go&#xff08;也称为Golang&#xff09;是一种开源的编程语言&#xff0c;由Google在2007年启动的项目中开发而来。它是一种静态类型的编译型语言&#xff0c;旨在提供高效、可靠的性能。相比于其他编程语言&#xff0c;Golang具有更高的执行效率和并发能力&#xff…

物理笔记

\(P\cdot V=N\cdot K\cdot T\) \(N\) 为分子数量 \(K\) 为常量 \(1.38\times 10^{-23} J\cdot K^{-1}\) 现在推到温度 \(T\) 代表分子平均动能。 考虑单个分子在正方体内运动 \(\Delta p=2mv_x\) \(F=\frac{\Delta p}{…

基于Python+Vue开发的商城管理系统源码+运行步骤

项目简介该项目是基于Python+Vue开发的商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Pyt…

HTML5-和-CSS3-迁移即时入门-全-

HTML5 和 CSS3 迁移即时入门(全)原文:zh.annas-archive.org/md5/94106B0DE1B83990A3B43B022F07C0DB 译者:飞龙 协议:CC BY-NC-SA 4.0前言 采用新技术总是一个具有挑战性的过程,特别是当它被视为对流行和广为认可…

HTML5-多人游戏开发-全-

HTML5 多人游戏开发(全)原文:zh.annas-archive.org/md5/58B015FFC16EF0C30C610502BF4A7DA3 译者:飞龙 协议:CC BY-NC-SA 4.0前言 欢迎来到《使用 HTML5 开发多人游戏》。本书将教你如何开发支持多个玩家在同一游戏…

HTML5-地理位置即时操作指南-全-

HTML5 地理位置即时操作指南(全)原文:zh.annas-archive.org/md5/d561e9d990e59031e96fb80bd9bd24f6 译者:飞龙 协议:CC BY-NC-SA 4.0前言 欢迎来到 Instant HTML5 Geolocation How-to。本指南将帮助你快速轻松地使…

暖色网站专项培训网站建设方案

51单片机—————8位单片机 裸机驱动 无系统 linux驱动 有系统 驱动-----反映硬件变化 MCU 微控器 MPU CPU GPU 图像处理 IDE 集成开发环境 peripheral 外设 SOC&#xff1a; system on chip P0&#xff1a;8bit——8个引脚 位运算 & …

哪里有免费的网站网址商城网站栏目

声明 下面的题目作答都是自己认为正确的答案&#xff0c;并非官方答案&#xff0c;如果有不同的意见&#xff0c;可以评论区交流。 这些题目也是笔者从各个地方收集的&#xff0c;感觉有些题目答案并不正确&#xff0c;所以在个别题目会给出自己的见解&#xff0c;欢迎大家讨论…

[Kernel] - Heterogeneous Memory Management (HMM)

[Kernel] - Heterogeneous Memory Management (HMM)Heterogeneous Memory Management (HMM) https://www.kernel.org/doc/html/latest/mm/hmm.html Provide infrastructure and helpers to integrate non-conventional…

GreenPlum - Get field types

GreenPlum - Get field types import psycopg2conn = psycopg2.connect(dbname="your_db",user="your_user",password="your_password",host="your_host",port="5432&qu…

搭建环境

环境的流程 一、介绍环境(在linux搭建) 多有米 jdk+linux+服务器(tomcat)+mysql+代码包 jdk 1.8版本 linux(centos) 服务器 tomcat(目前用) 、apache、nginx 数据库 mysql 数据包(后缀 是.mysql)…

山东建设厅科技处网站python官网下载安装

写在前面 如果只有一个kafka实例的话&#xff0c;那么文章中提到kafka集群kafka实例 一、什么是消息发送者端的ack机制 ack机制&#xff1a;消息确认发送成功的标识 由谁发起该标识&#xff1a;kafka集群 发起该标识的场景&#xff1a;kafka集群确认已经收到了消息。 由谁接收…

20250928

周末了 看了下棕榈 前期好像走了个小五波吧 要涨么?不过大周期看还有个Yc的波段

Easysearch 国产替代 Elasticsearch:8 大核心挑战解读

Easysearch 国产替代 Elasticsearch:8 大核心挑战解读pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&q…

Typescript概述和思维导图

TypeScript 简要概述 TypeScript 是由微软开发的开源编程语言,它是 JavaScript 的一个超集。这意味着任何有效的 JavaScript 代码也都是有效的 TypeScript 代码。 核心价值在于其强大的静态类型系统。主要特点:静态类…

9-28

初态只有一个,终态可以有多个 自动机是进行词法分析的工具

Qt结合ffmpeg代码实现udp推流/组播推流/rtp推流/监控GB28181推流/onvif推流

一、前言说明 之前已经用ffmpeg代码实现了rtsp和rtmp推流,在没有搞过推流的时候,以为很难,其实推流就是保存文件到一个rtsp/rtmp地址,完全复用保存到MP4文件的代码,唯一不同的时候就是在avformat_alloc_output_co…

个人网站建设多少钱网站建设翻译插件

矩阵中的路径 题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是…

新乡住房与城乡建设厅网站xsxz wordpress

近日&#xff0c;腾讯的大动作一个接一个&#xff0c;前脚刚公布2023上半年财报&#xff0c;后脚就开启了2024校招&#xff0c;不得不让人感叹腾讯真速度&#xff01; 此次招聘对象为毕业时间在2023年9月至2024年8月期间的2024届应届毕业生&#xff0c;覆盖北上广深等多个城市…