如何使用API Key与Secret保护无用户认证的Spring Boot API
我之前刚好帮朋友实现过一模一样的需求——不需要用户登录,只靠API Key/Secret来限制特定客户端访问Spring Boot接口,给你一套完整的落地方案:
具体实现方案
1. 配置API Key和Secret
首先把你的API密钥对存在配置文件里,绝对不要硬编码到代码中。在application.yml里添加:
api: auth: key: YOUR_UNIQUE_API_KEY # 替换成你自己的唯一Key secret: YOUR_STRONG_API_SECRET # 替换成你自己的高强度Secret
然后创建一个配置类来读取这些值,方便后续逻辑调用:
@ConfigurationProperties(prefix = "api.auth") @Component @Data // 用Lombok简化getter/setter,没有Lombok的话手动写也可以 public class ApiAuthProperties { private String key; private String secret; }
2. 编写认证拦截器
用Spring的HandlerInterceptor实现请求前的校验逻辑,拦截所有接口请求,检查请求头里的API密钥对是否有效:
@Component public class ApiAuthInterceptor implements HandlerInterceptor { private final ApiAuthProperties apiAuthProperties; // 构造注入配置类 public ApiAuthInterceptor(ApiAuthProperties apiAuthProperties) { this.apiAuthProperties = apiAuthProperties; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从请求头获取客户端传来的Key和Secret String requestApiKey = request.getHeader("X-API-Key"); String requestApiSecret = request.getHeader("X-API-Secret"); // 第一步:检查密钥对是否为空 if (StringUtils.isEmpty(requestApiKey) || StringUtils.isEmpty(requestApiSecret)) { sendErrorResponse(response, HttpStatus.UNAUTHORIZED, "Missing API Key or Secret"); return false; } // 第二步:检查密钥对是否匹配配置值 if (!apiAuthProperties.getKey().equals(requestApiKey) || !apiAuthProperties.getSecret().equals(requestApiSecret)) { sendErrorResponse(response, HttpStatus.UNAUTHORIZED, "Invalid API Key or Secret"); return false; } // 校验通过,放行请求 return true; } // 统一返回JSON格式的错误响应 private void sendErrorResponse(HttpServletResponse response, HttpStatus status, String message) throws IOException { response.setStatus(status.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\": " + status.value() + ", \"message\": \"" + message + "\"}"); } }
这里用到的StringUtils是Spring Core里的工具类,Spring Boot项目默认已经包含依赖。
3. 注册拦截器,指定拦截范围
创建一个WebMvc配置类,把刚才的拦截器注册进去,明确要拦截的接口路径(比如所有/api/**开头的接口),如果有不需要校验的公开接口(比如健康检查),可以直接排除:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { private final ApiAuthInterceptor apiAuthInterceptor; public WebMvcConfig(ApiAuthInterceptor apiAuthInterceptor) { this.apiAuthInterceptor = apiAuthInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(apiAuthInterceptor) .addPathPatterns("/api/**") // 拦截所有API接口,根据你的实际路径调整 .excludePathPatterns("/api/public/health"); // 排除公开的健康检查接口 } }
4. 客户端调用方式
你的第三方前端在调用API时,只需要在请求头里带上X-API-Key和X-API-Secret两个字段即可,比如用Axios的示例:
axios.get('/api/data', { headers: { 'X-API-Key': 'YOUR_UNIQUE_API_KEY', 'X-API-Signature': 'YOUR_STRONG_API_SECRET' } })
可选优化:更安全的签名机制
如果担心Secret在网络传输中被窃取,可以改用请求签名的方式,Secret不会直接在请求中传输:
- 客户端生成当前时间戳
timestamp,将请求参数(或请求体JSON)+时间戳用Secret做HMAC-SHA256加密,得到signature - 客户端请求时携带
X-API-Key、X-API-Timestamp、X-API-Signature三个请求头 - 服务端先校验时间戳是否在有效期内(比如5分钟),防止重放攻击;再用同样的规则计算签名,和客户端传来的对比是否一致
这种方式即使Key被泄露,没有Secret也无法生成有效的签名,安全性会更高。
内容的提问来源于stack exchange,提问作者Vitalii Oleksiv
相关产品推荐
相关产品推荐

