1. 项目概述为什么需要让若依“裸奔”最近在几个项目对接的场景里我反复被问到同一个问题“我们有一套基于若依RuoYi快速搭建的内部管理系统现在想开放几个API给外部的合作伙伴或者移动端App调用但每次调用都卡在登录和权限校验上能不能把权限验证去掉让外部系统直接访问” 这其实是一个非常典型且高频的需求。若依作为一个功能强大、开箱即用的后台管理框架其核心设计理念就是为内部、可控、有明确角色划分的管理员用户服务。因此它默认集成了Spring Security或Shiro从登录拦截、菜单权限到数据权限构建了一套严密的安全防线。然而当业务发展到需要与外部生态对接时这套“铜墙铁壁”就成了障碍。想象一下一个供应商需要实时查询订单状态一个IoT设备需要定时上报数据或者一个H5活动页面需要拉取最新的公告列表——你不可能要求一个设备或者一个外部合作方的系统先去你的若依登录页面上输入用户名密码。这时候我们就需要让若依的某些特定接口“裸奔”出来即在不破坏整体安全架构的前提下为特定的外部调用开辟一条免认证、免鉴权的通道。这绝不是简单地删掉RequiresPermissions注解或者注释掉Security配置那么简单它涉及到对若依安全体系的理解、对请求生命周期的把控以及如何在开放与安全之间找到精妙的平衡点。接下来我就结合自己多次“改造”若依的经验把这个过程的思路、步骤和坑点掰开揉碎了讲清楚。2. 核心思路拆解白名单、过滤器与安全边界直接“去掉权限”是一个危险且不严谨的说法。我们的目标不是摧毁若依的安全体系而是在这套体系上开几个受控的、安全的“后门”。核心思路可以归纳为三点识别、放行、保护。2.1 识别如何定义“外部调用”首先我们需要一个清晰的规则来区分“内部管理请求”和“外部API请求”。常见策略有URL路径前缀这是最直观的方式。例如约定所有以/api/open/开头的请求都是对外开放的接口。若依的控制器Controller可以统一放在这个路径下。自定义请求头要求外部调用方在请求中携带一个特定的Header例如X-Client-Type: external。这种方式更灵活但需要调用方配合。独立的端口或服务为对外开放的API单独部署一个服务实例与内部管理后台完全隔离。这是最彻底、最安全的方式但运维成本较高。对于大多数场景采用URL路径前缀是最佳实践。它规则简单在若依的过滤器链中很容易进行匹配和判断也便于后续的监控和日志统计。2.2 放行在安全链的哪个环节“开口子”若依的权限校验通常嵌入在Spring Security的过滤器链中。一个HTTP请求的典型处理流程是先经过一系列过滤器Filter然后到达DispatcherServlet最终由对应的Controller处理。权限校验如PreAuthorize发生在控制器方法执行之前但通常在过滤器层面就已经进行了登录状态Session或Token的检查。因此我们的放行点必须在权限校验过滤器之前。我们需要一个自定义的过滤器把它放在安全过滤器链的最前端。这个过滤器的职责就是检查当前请求的URL是否匹配我们定义的“开放接口”规则。如果匹配则直接“放行”chain.doFilter让请求跳过后续所有的安全校验直达业务控制器如果不匹配则交给后续的Spring Security过滤器链按原有流程处理。2.3 保护开放不等于不设防去掉登录和菜单权限校验不代表接口可以任意滥用。我们必须为这些开放接口建立新的、适合外部场景的安全边界限流与防刷使用如Sentinel、Redis等工具基于IP或客户端标识对接口进行访问频率限制防止恶意刷接口导致服务瘫痪。简单令牌校验虽然不用复杂的用户体系但可以设计一个简单的API Key或签名机制。例如要求请求携带一个预先分配好的apiKey服务端进行校验或者对请求参数、时间戳进行MD5/SHA256签名防止请求被篡改。输入校验与SQL注入防护外部输入不可信。必须在Controller层或通过Valid注解对入参进行严格校验并使用MyBatis的#{}预编译方式防止SQL注入。日志与监控详细记录开放接口的访问日志包括IP、请求参数、响应时间、状态等便于事后审计和问题排查。核心原则从“基于角色的访问控制RBAC”转向“基于客户端身份的轻量级认证与授权”。安全的重心从“谁用户能做什么菜单/按钮”转变为“哪个可信客户端可以调用哪个接口频率多高”。3. 实操步骤三步实现若依接口对外开放下面我们以最常见的Spring Boot Spring Security版本的若依为例演示如何通过自定义过滤器实现接口开放。假设我们的开放接口路径统一为/api/open/**。3.1 第一步创建开放接口控制器首先在若依项目中创建一个新的包例如com.ruoyi.project.open.controller用于存放所有对外开放的控制器。这样做有利于代码隔离和管理。package com.ruoyi.project.open.controller; import com.ruoyi.common.core.domain.AjaxResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 对外开放的示例接口控制器 * 注意这个控制器下的所有接口理论上都应跳过Spring Security的权限校验 */ RestController RequestMapping(/api/open) public class OpenApiController { /** * 示例一个无需任何权限即可访问的公共信息接口 * return */ GetMapping(/publicInfo) public AjaxResult getPublicInfo() { // 这里可以是从数据库查询的公告、配置等非敏感信息 return AjaxResult.success(这是一个对外开放的接口无需登录即可访问。当前时间 System.currentTimeMillis()); } /** * 示例一个需要简单API Key校验的接口 * 在实际项目中校验逻辑可以放在过滤器中统一处理 * param apiKey * return */ GetMapping(/data) public AjaxResult getSomeData(String apiKey) { // 简单的API Key校验仅为示例生产环境应更复杂 if (!PRE_SHARED_SECRET_KEY_123.equals(apiKey)) { return AjaxResult.error(无效的API Key); } return AjaxResult.success(成功访问数据接口你的API Key是: apiKey); } }关键点这些控制器的方法上不要添加任何若依的权限注解如RequiresPermissions或PreAuthorize。它们就是普通的Spring MVC控制器方法。3.2 第二步创建并配置自定义放行过滤器这是最核心的一步。我们需要创建一个过滤器来识别并放行对/api/open/**的请求。package com.ruoyi.project.open.filter; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.List; /** * 开放API路径过滤器 * 用于放行指定的公开接口使其绕过Spring Security的认证和授权流程 * Order 注解确保此过滤器在Spring Security过滤器链之前执行 */ Component Slf4j Order(Ordered.HIGHEST_PRECEDENCE) // 设置为最高优先级确保最先执行 public class OpenApiPathFilter implements Filter { /** * 定义需要放行的公开接口路径模式列表 * 使用Ant风格路径匹配符 * ? 匹配一个字符 * * 匹配0个或多个字符不跨越路径分隔符 * ** 匹配0个或多个目录 */ private static final ListString OPEN_API_PATTERNS Arrays.asList( /api/open/**, // 可以在这里添加更多开放路径例如Swagger文档、健康检查等 /swagger-ui/**, /v3/api-docs/**, /actuator/health ); private final PathMatcher pathMatcher new AntPathMatcher(); Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest (HttpServletRequest) request; HttpServletResponse httpResponse (HttpServletResponse) response; String requestURI httpRequest.getRequestURI(); // 判断当前请求是否匹配开放路径 boolean isOpenApi OPEN_API_PATTERNS.stream() .anyMatch(pattern - pathMatcher.match(pattern, requestURI)); if (isOpenApi) { // 对于开放API直接放行跳过后续所有过滤器包括Spring Security的过滤器 log.debug(放行开放API请求: {}, requestURI); chain.doFilter(request, response); } else { // 对于非开放API继续执行过滤器链最终会进入Spring Security的流程 chain.doFilter(request, response); } } Override public void init(FilterConfig filterConfig) throws ServletException { log.info(开放API路径过滤器初始化完成。放行模式: {}, OPEN_API_PATTERNS); } Override public void destroy() { // 清理资源如有 } }关键点解析Order(Ordered.HIGHEST_PRECEDENCE)这个注解至关重要。它确保我们的OpenApiPathFilter在Spring Security的过滤器特别是UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter等之前执行。只有这样我们“放行”的请求才不会触发登录验证。Ant路径匹配使用AntPathMatcher可以灵活地匹配路径模式比如/api/open/**可以匹配/api/open/publicInfo和/api/open/v1/data等所有子路径。日志记录在放行时记录日志便于调试和监控。重要提示仅仅放行过滤器还不够。Spring Security的配置可能会对所有路径进行安全约束。我们还需要修改Security配置明确告诉Spring Security忽略对这些开放路径的拦截。3.3 第三步修改Spring Security配置找到若依项目中Spring Security的配置类通常是SecurityConfig或WebSecurityConfig。我们需要在configure(WebSecurity web)或configure(HttpSecurity http)方法中将开放路径排除在安全规则之外。推荐方式在configure(WebSecurity web)中忽略Configuration EnableGlobalMethodSecurity(prePostEnabled true, securedEnabled true) public class SecurityConfig extends WebSecurityConfigurerAdapter { // ... 其他配置如密码编码器、用户详情服务等 /** * 配置WebSecurity用于忽略对静态资源和公开接口的安全控制 * 此配置的优先级高于HttpSecurity被忽略的路径将完全绕过Spring Security过滤器链 * param web */ Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers( // 开放API路径 /api/open/**, // Swagger文档 /swagger-ui.html, /swagger-ui/**, /v3/api-docs/**, // 健康检查 /actuator/health, // 静态资源若依原有配置 /css/**, /js/**, /img/**, /profile/** ); } /** * 配置HttpSecurity定义核心的安全规则登录、授权、会话管理等 * 此配置对未被 web.ignoring() 忽略的路径生效 * param http */ Override protected void configure(HttpSecurity http) throws Exception { http // ... 若依原有的CSRF、表单登录、会话管理等配置 .authorizeRequests() // 定义其他所有请求都需要认证除了上面忽略的 .anyRequest().authenticated() .and() // ... 其他配置如登录页、登出处理等 } }为什么这样做web.ignoring().antMatchers(...)的作用是让Spring Security完全忽略对这些路径的防护这些请求甚至不会进入Spring Security的过滤器链。这与我们自定义过滤器的目标一致且是Spring Security官方推荐的处理静态资源或完全公开接口的方式。双重保障自定义过滤器Security忽略更加可靠。备选方式在configure(HttpSecurity http)中放行如果你更倾向于在HttpSecurity中统一管理所有路径规则也可以这样做Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 首先明确放行公开接口允许匿名访问 .antMatchers(/api/open/**).permitAll() .antMatchers(/swagger-ui/**).permitAll() // ... 其他放行规则 // 然后定义其他所有请求都需要认证 .anyRequest().authenticated() .and() // ... 其他配置 }两种方式的主要区别在于web.ignoring()是彻底绕过Security过滤器链性能稍好而在HttpSecurity中permitAll()请求仍然会经过Security过滤器链只是最终授权判断为“允许”更便于在过滤器中添加一些公共逻辑如添加安全头。对于纯粹的开放API使用web.ignoring()更简洁高效。4. 进阶安全与管控策略接口开放后安全责任就从“权限框架”转移到了“我们自己的代码”上。以下是一些必须考虑的进阶策略。4.1 实现轻量级API签名校验对于需要一定安全级别的开放接口推荐使用签名机制。一个简单的基于HMAC-SHA256的签名流程如下分配凭证为每个外部调用方客户端分配一个唯一的clientId和一个保密的clientSecret。生成签名客户端在调用时需要生成签名。将请求方法GET/POST、请求路径、时间戳、随机字符串nonce和请求参数按字母序排序拼接成一个待签名字符串。使用clientSecret通过HMAC-SHA256算法计算待签名字符串的签名。传递签名将clientId、timestamp、nonce和计算得到的signature通过HTTP Header如X-Client-Id,X-Timestamp,X-Nonce,X-Signature发送到服务端。服务端验证防重放检查timestamp是否在合理时间窗口内如5分钟并检查nonce是否在短时间内未被使用可用Redis缓存已使用的nonce。身份验证根据clientId查找对应的clientSecret。签名验证使用同样的规则拼接字符串并用查到的clientSecret计算签名与客户端传来的signature比对。我们可以将这套验证逻辑实现在一个Spring拦截器Interceptor或另一个过滤器中并只将其应用到/api/open/**路径下需要签名的接口上。4.2 集成限流组件防止接口被刷是开放服务的生命线。集成Sentinel是最佳实践之一。引入依赖在pom.xml中添加Sentinel Starter依赖。定义资源在开放接口的方法上使用SentinelResource注解定义资源点。配置规则在Sentinel控制台或通过代码配置流控规则。例如对某个接口的每个clientId限制每秒10次调用。GetMapping(/sensitiveData) SentinelResource(value openApi:sensitiveData, blockHandler handleBlock) public AjaxResult getSensitiveData(RequestHeader(X-Client-Id) String clientId) { // 业务逻辑 return AjaxResult.success(敏感数据); } // 限流或降级处理函数 public AjaxResult handleBlock(String clientId, BlockException ex) { log.warn(接口被限流clientId: {}, clientId); return AjaxResult.error(请求过于频繁请稍后再试); }4.3 详细的访问日志与审计为所有开放接口的请求和响应记录详细的日志至少包括请求ID、客户端IP、clientId、请求URL、方法、参数、请求时间、响应时间、响应状态、耗时。这有助于问题排查当外部调用方反馈问题时可以快速定位日志。安全审计分析异常访问模式如某个客户端突然请求量暴增。数据分析了解接口使用情况。可以考虑使用Spring的HandlerInterceptor或Servlet Filter在请求前后记录日志并将关键信息如clientId放入MDCMapped Diagnostic Context以便在日志中统一输出。5. 常见问题与避坑指南在实际操作中你可能会遇到以下问题5.1 问题一自定义过滤器不生效请求依然被拦截到登录页可能原因1过滤器顺序不对。确保你的过滤器使用了Order(Ordered.HIGHEST_PRECEDENCE)或通过FilterRegistrationBean手动设置了最高优先级并且确实在Spring Security的过滤器之前执行。可以通过在过滤器的doFilter方法开始和结束处打日志来确认。可能原因2Spring Security配置未忽略对应路径。检查SecurityConfig中的web.ignoring()或permitAll()配置确保路径模式写对了。特别注意Ant路径的写法/api/open/*和/api/open/**是不同的。排查步骤在过滤器的doFilter方法第一行打印requestURI看请求是否进入了你的过滤器。检查Spring Boot的启动日志看过滤器注册的顺序。暂时在SecurityConfig的configure(HttpSecurity http)方法最开头加上http.authorizeRequests().anyRequest().permitAll();来测试是否是Security配置的问题。5.2 问题二开放接口能访问但若依原有的登录用户信息获取报错场景在开放接口的Controller中你尝试通过SecurityContextHolder.getContext().getAuthentication()或者若依的getLoginUser()方法获取当前用户结果报空指针或获取到匿名用户。原因这是符合预期的。因为开放接口跳过了Spring Security的认证流程SecurityContext中自然没有认证信息。开放接口不应依赖任何会话或用户上下文。解决方案所有需要的业务参数必须通过请求参数RequestParam、PathVariable、RequestBody显式传递。如果需要客户端身份使用我们前面提到的clientId和签名机制从Header或参数中解析而不是从Security上下文中获取。5.3 问题三如何管理大量的开放接口和客户端凭证挑战随着开放接口增多clientId/clientSecret对也会增加硬编码在代码或配置文件中难以维护。解决方案建立简单的管理模块在若依系统内新增一个“开放平台管理”菜单仅限管理员。功能包括客户端应用的新增、禁用、密钥重置API接口的查询与状态管理。将凭证存储在数据库表中。缓存优化在验证签名时频繁查数据库会影响性能。可以将有效的clientId和对应的clientSecret缓存到Redis中并设置合理的过期时间。使用成熟的API网关对于中大型项目强烈建议引入独立的API网关如Spring Cloud Gateway、Kong、Apisix。将开放接口全部迁移到网关后面由网关统一负责认证、鉴权、限流、监控、日志若依后端服务则彻底回归内部角色。这是最清晰、最专业的架构。5.4 问题四Swagger文档也变成了无需登录即可访问现象按照上述配置Swagger的路径/swagger-ui/**也被放行了这可能导致内部API文档暴露。处理这是有意为之的因为开发阶段需要方便访问。在生产环境你必须通过环境配置来禁用Swagger或限制其访问。禁用在application-prod.yml中设置swagger.enabledfalse。IP白名单更安全的方式是不在web.ignoring()中放行Swagger而是通过Spring Security配置只允许来自内网IP的请求访问Swagger路径。这需要对Security配置做更精细的控制。让若依接口安全地对接到外部世界关键在于理解其安全框架的工作原理并在合适的环节进行精准的“外科手术式”修改而不是粗暴地关闭整个安全系统。通过“自定义过滤器识别放行 Spring Security配置忽略 轻量级客户端认证”的组合拳你可以在享受若依开发效率的同时灵活地构建面向外部的API服务。记住开放之后安全的重心就从框架转移到了你的业务逻辑设计和基础设施保障上限流、监控、审计一个都不能少。