api key验证应放在自定义中间件而非actionfilter,因其在路由前拦截请求、降低开销且覆盖所有端点;推荐使用authorization: apikey头提取密钥;生产环境须安全存储密钥并防时序攻击;避免滥用[authorize],校验后仅设标记或轻量认证方案。

API Key验证该放在哪里:ActionFilter还是中间件
在ASP.NET Core中,API Key验证最合理的位置是自定义中间件(Middleware),而不是ActionFilter。因为ActionFilter在路由匹配之后才执行,此时请求已进入MVC管道,而密钥校验属于「接入层鉴权」,应尽早拦截非法请求,避免路由解析、模型绑定等开销。
中间件能统一处理所有端点(包括Controllers、Minimal API、静态文件等),也更容易与HttpContext生命周期解耦。
如何从请求中提取API Key:Header、Query还是Custom Scheme
推荐使用Authorization头配合自定义方案(如ApiKey),格式为:Authorization: ApiKey <your-key></your-key>。这样既符合HTTP规范,又避免和Bearer、Basic混淆,也比X-Api-Key头更易被网关/代理识别。
不建议用查询参数(?api_key=xxx),因为会泄漏到日志、CDN缓存、浏览器历史中;也不建议硬编码X-Api-Key头名,它缺乏标准化语义,且容易被某些反向代理默认剥离。
提取逻辑示例:
var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
if (authHeader?.StartsWith("ApiKey ", StringComparison.OrdinalIgnoreCase) == true)
{
var key = authHeader["ApiKey ".Length..].Trim();
}
密钥存储与校验要注意什么:硬编码、配置还是数据库
开发阶段可暂存于appsettings.json或环境变量,但上线必须用安全方式管理:
- 生产环境禁用
appsettings.Development.json中的明文密钥 - 避免在代码里写死
"MySecretApiKey123"——编译后可能被反编译提取 - 若需多租户或多密钥,优先走数据库+缓存(如
IDistributedCache),而非每次查库 - 密钥建议用随机生成的32字节以上Base64字符串,不用UUID(可预测性高)
校验时务必使用TimeConstantComparer或CryptographicOperations.FixedTimeEquals防止时序攻击,普通==比较不安全。
为什么不能只靠[Authorize]和Policy:API Key不是Identity
[Authorize]依赖IAuthenticationService和ClaimsPrincipal,而API Key本身不含用户身份信息,强行塞进ClaimsIdentity会导致后续IsAuthenticated返回true,但User.Identity.Name为空——这会让下游权限逻辑误判。
更稳妥的做法是:中间件校验通过后,仅设置一个标记(如context.Items["ValidApiKey"] = true),再由后续业务逻辑按需读取;或者注册一个轻量AuthenticationScheme(如AddAuthentication().AddScheme<apikeyauthenticationoptions apikeyauthenticationhandler></apikeyauthenticationoptions>),但不要注入完整ClaimsPrincipal。
真正容易被忽略的是错误响应体:返回401 Unauthorized时,务必省略WWW-Authenticate头(除非你真实现了标准认证流程),否则某些客户端会自动弹登录框或重定向。










