API Key 身份验证
必需的依赖项:io.ktor:ktor-server-auth, io.ktor:ktor-server-auth-api-key
API Key 身份验证是一种简单的身份验证方法,客户端在请求中(通常是在标头中)传递一个私密密钥。该密钥既作为标识符,也作为身份验证机制。
Ktor 允许您使用 API Key 身份验证来保护路由并验证客户端请求。
您可以在Ktor Server 中的身份验证与授权部分获取有关 Ktor 身份验证的一般信息。
API Key 应当保密并安全传输。建议使用 HTTPS/TLS 来保护传输中的 API Key。
添加依赖项
要启用 API Key 身份验证,请在构建脚本中添加 ktor-server-auth 和 ktor-server-auth-api-key 构件:
implementation("io.ktor:ktor-server-auth-api-key:$ktor_version")
implementation("io.ktor:ktor-server-auth:$ktor_version")implementation "io.ktor:ktor-server-auth-api-key:$ktor_version"
implementation "io.ktor:ktor-server-auth:$ktor_version"<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-server-auth-api-key-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-server-auth</artifactId>
<version>${ktor_version}</version>
</dependency>API Key 身份验证流程
API Key 身份验证流程如下:
- 客户端向服务器应用程序中的特定路由发起请求,请求头中包含 API Key(通常为
X-API-Key)。 - 服务器使用自定义验证逻辑验证该 API Key。
- 如果密钥有效,服务器将响应所请求的内容。如果密钥无效或缺失,服务器将响应
401 Unauthorized状态。
安装 API Key 身份验证
要安装 apiKey 身份验证提供者,请在 install(Authentication) 块内调用 apiKey 函数:
import io.ktor.server.application.*
import io.ktor.server.auth.*
// ...
install(Authentication) {
apiKey {
// 配置 API Key 身份验证
}
}您可以选择性地指定一个提供者名称,用于对指定路由进行身份验证。
配置 API Key 身份验证
在本节中,我们将了解 apiKey 身份验证提供者的具体配置。
要了解如何在 Ktor 中配置不同的身份验证提供者,请参阅配置身份验证。
第 1 步:配置 API Key 提供者
apiKey 身份验证提供者通过 ApiKeyAuthenticationProvider.Config 类公开其设置。在下面的示例中,指定了以下设置:
validate函数接收从请求中提取的 API Key,并在身份验证成功时返回Principal,如果身份验证失败则返回null。
这是一个最小示例:
data class AppPrincipal(val key: String) : Principal
install(Authentication) {
apiKey {
validate { keyFromHeader ->
val expectedApiKey = "this-is-expected-key"
keyFromHeader
.takeIf { it == expectedApiKey }
?.let { AppPrincipal(it) }
}
}
}自定义密钥位置
默认情况下,apiKey 提供者会在 X-API-Key 标头中查找 API Key。
您可以使用 headerName 指定自定义标头:
apiKey("api-key-header") {
headerName = "X-Secret-Key"
validate { key ->
// ...
}
}第 2 步:验证 API Key
验证逻辑取决于您的应用程序需求。以下是常见的方法:
静态密钥比较
对于简单的情况,您可以与预定义的密钥进行比较:
apiKey {
validate { keyFromHeader ->
val expectedApiKey = environment.config.property("api.key").getString()
keyFromHeader
.takeIf { it == expectedApiKey }
?.let { AppPrincipal(it) }
}
}请将敏感的 API Key 存储在配置文件或环境变量中,不要存储在源代码中。
数据库查询
对于多个 API Key,请针对数据库进行验证:
apiKey {
validate { keyFromHeader ->
// 在数据库中查找密钥
val user = database.findUserByApiKey(keyFromHeader)
user?.let { UserIdPrincipal(it.username) }
}
}多项验证标准
您可以实现复杂的验证逻辑:
apiKey {
validate { keyFromHeader ->
val apiKey = database.findApiKey(keyFromHeader)
// 检查密钥是否存在、是否处于活动状态以及是否未过期
if (apiKey != null &&
apiKey.isActive &&
apiKey.expiresAt > Clock.System.now()
) {
UserIdPrincipal(apiKey.userId)
} else {
null
}
}
}第 3 步:配置 Challenge
您可以使用 challenge 函数自定义身份验证失败时发送的响应:
apiKey {
validate { key ->
// 验证逻辑
}
challenge { defaultScheme, realm ->
call.respond(
HttpStatusCode.Unauthorized,
"Invalid or missing API key"
)
}
}第 4 步:保护特定资源
配置 apiKey 提供者后,您可以使用 authenticate 函数保护应用程序中的特定资源。在身份验证成功的情况下,您可以在路由处理程序中使用 call.principal 函数检索经过身份验证的 Principal。
routing {
authenticate {
get("/") {
val principal = call.principal<AppPrincipal>()!!
call.respondText("Hello, authenticated client! Your key: ${principal.key}")
}
}
}API Key 身份验证示例
这是一个完整的 API Key 身份验证最小示例:
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
data class AppPrincipal(val key: String) : Principal
fun Application.module() {
val expectedApiKey = "this-is-expected-key"
install(Authentication) {
apiKey {
validate { keyFromHeader ->
keyFromHeader
.takeIf { it == expectedApiKey }
?.let { AppPrincipal(it) }
}
}
}
routing {
authenticate {
get("/") {
val principal = call.principal<AppPrincipal>()!!
call.respondText("Key: ${principal.key}")
}
}
}
}最佳做法
在实现 API Key 身份验证时,请考虑以下最佳做法:
- 使用 HTTPS:始终通过 HTTPS 传输 API Key 以防止拦截。
- 安全存储:切勿在源代码中硬编码 API Key。请使用环境变量或安全配置管理。
- 密钥轮换:实现定期轮换 API Key 的机制。
- 速率限制:将 API Key 身份验证与速率限制结合使用以防止滥用。
- 日志记录:记录身份验证失败以便进行安全监控,但切勿记录真实的 API Key。
- 密钥格式:为 API Key 使用加密安全的随机字符串(例如 UUID 或 base64 编码的随机字节)。
- 多重密钥:考虑为每个用户针对不同的应用程序或用途支持多个 API Key。
- 过期:实现密钥过期以增强安全性。
