Ktor Server 中的摘要认证
必需的依赖项: io.ktor:ktor-server-auth
代码示例: auth-digest
摘要认证方案是 HTTP 框架的一部分,用于访问控制和认证。在此方案中,在通过网络发送用户名和密码之前,会对其应用哈希函数。
Ktor 允许您使用摘要认证来登录用户和保护特定的路由。您可以在 Ktor Server 中的认证与授权章节中获取有关 Ktor 认证的一般信息。
添加依赖项
要启用 digest
认证,您需要在构建脚本中包含 ktor-server-auth
构件:
摘要认证流程
摘要认证流程如下所示:
客户端向服务器应用程序中的特定路由发起不带
Authorization
请求头的请求。服务器向客户端响应
401
(Unauthorized) 响应状态,并使用WWW-Authenticate
响应头提供信息,表明该路由使用了摘要认证方案。一个典型的WWW-Authenticate
头如下所示:WWW-Authenticate: Digest realm="Access to the '/' path", nonce="e4549c0548886bc2", algorithm="MD5"
在 Ktor 中,您可以在配置
digest
认证提供者时指定 realm 和生成 nonce 值的方式。通常,客户端会显示一个登录对话框,用户可以在其中输入凭据。然后,客户端发起一个带有以下
Authorization
请求头的请求:Authorization: Digest username="jetbrains", realm="Access to the '/' path", nonce="e4549c0548886bc2", uri="/", algorithm=MD5, response="6299988bb4f05c0d8ad44295873858cf"
response
值通过以下方式生成:a.
HA1 = MD5(username:realm:password)
这部分存储在服务器上,Ktor 可以用它来验证用户凭据。
b.
HA2 = MD5(method:digestURI)
c.
response = MD5(HA1:nonce:HA2)
服务器验证客户端发送的凭据并响应所请求的内容。
安装摘要认证
要安装 digest
认证提供者,请在 install
代码块中调用 digest 函数:
import io.ktor.server.application.*
import io.ktor.server.auth.*
// ...
install(Authentication) {
digest {
// Configure digest authentication
}
}
您可以选择性地指定一个提供者名称,该名称可用于认证指定的路由。
配置摘要认证
要大致了解如何在 Ktor 中配置不同的认证提供者,请参阅配置认证。在本节中,我们将重点介绍 digest
认证提供者的配置细节。
步骤 1:提供包含摘要的用户表
digest
认证提供者使用摘要消息的 HA1
部分来验证用户凭据。因此,您可以提供一个包含用户名和对应的 HA1
哈希值的用户表。在下面的示例中,getMd5Digest
函数用于生成 HA1
哈希值:
fun getMd5Digest(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray(UTF_8))
val myRealm = "Access to the '/' path"
val userTable: Map<String, ByteArray> = mapOf(
"jetbrains" to getMd5Digest("jetbrains:$myRealm:foobar"),
"admin" to getMd5Digest("admin:$myRealm:password")
)
步骤 2:配置摘要提供者
digest
认证提供者通过 DigestAuthenticationProvider.Config 类公开其设置。在下面的示例中,指定了以下设置:
realm
属性设置要传递到WWW-Authenticate
请求头中的 realm。digestProvider
函数获取指定用户名的摘要的HA1
部分。- (可选)
validate
函数允许您将凭据映射到自定义 Principal。
fun Application.main() {
install(Authentication) {
digest("auth-digest") {
realm = myRealm
digestProvider { userName, realm ->
userTable[userName]
}
validate { credentials ->
if (credentials.userName.isNotEmpty()) {
CustomPrincipal(credentials.userName, credentials.realm)
} else {
null
}
}
}
}
}
data class CustomPrincipal(val userName: String, val realm: String)
您还可以使用 nonceManager 属性来指定如何生成 nonce 值。
步骤 3:保护特定资源
配置 digest
提供者后,您可以使用 authenticate 函数保护应用程序中的特定资源。如果认证成功,您可以在路由处理程序中使用 call.principal
函数检索经过认证的 Principal,并获取认证用户的名称。
routing {
authenticate("auth-digest") {
get("/") {
call.respondText("Hello, ${call.principal<CustomPrincipal>()?.userName}!")
}
}
}