Skip to content
Server Plugin

Ktor 服务器中的认证与授权

必需的依赖项: io.ktor:ktor-server-auth

Ktor 提供了 Authentication 插件来处理认证和授权。典型使用场景包括用户登录、授予对特定资源的访问权限以及在各方之间安全地传输信息。您还可以将 AuthenticationSessions 结合使用,以在请求之间保留用户的会话信息。

在客户端,Ktor 提供了 Authentication 插件用于处理认证和授权。

支持的认证类型

Ktor 支持以下认证和授权方案:

HTTP 认证

HTTP 提供了 通用框架 用于访问控制和认证。在 Ktor 中,您可以使用以下 HTTP 认证方案:

  • Basic - 使用 Base64 编码提供用户名和密码。通常不建议单独使用,除非与 HTTPS 结合使用。
  • Digest - 一种认证方法,通过对用户名和密码应用哈希函数,以加密形式通信用户凭据。
  • Bearer - 一种涉及称为 bearer 令牌的安全令牌的认证方案。 Bearer 认证方案作为 OAuthJWT 的一部分使用,但您也可以为授权 bearer 令牌提供自定义逻辑。

基于表单的认证

基于表单的认证使用 Web 表单 收集凭据信息并认证用户。

JSON Web Token (JWT)

JSON Web Token 是一种开放标准,用于将信息作为 JSON 对象在各方之间安全传输。您可以将 JSON Web Token 用于授权:当用户登录后,每个请求都将包含一个 token,允许用户访问该 token 允许的资源。在 Ktor 中,您可以使用 jwt 认证来验证 token 并校验其中包含的声明。

LDAP

LDAP 是一种开放且跨平台的协议,用于目录服务认证。Ktor 提供了 ldapAuthenticate 函数,用于根据指定的 LDAP 服务器认证用户凭据。

OAuth

OAuth 是一种用于保护 API 访问安全的开放标准。Ktor 中的 oauth 提供者允许您使用 Google、Facebook、Twitter 等外部提供者实现认证。

Session

Sessions 提供了一种在不同 HTTP 请求之间持久化数据的机制。典型用例包括存储登录用户的 ID、购物车内容或在客户端保存用户偏好。在 Ktor 中,已有关联 session 的用户可以使用 session 提供者进行认证。关于如何实现,请参阅 Ktor 服务器中的 Session 认证

自定义

Ktor 还提供了用于创建 自定义插件 的 API,可用于实现您自己的插件来处理认证和授权。 例如,AuthenticationChecked 钩子 在认证凭据检测后执行,它允许您实现授权:custom-plugin-authorization

添加依赖项

要使用 Authentication,您需要在构建脚本中包含 ktor-server-auth artifact:

Kotlin
Groovy
XML

请注意,一些认证提供者,例如 JWTLDAP,需要额外的 artifacts。

安装 Authentication

安装 Authentication 插件到应用程序中, 请将其传递给指定

模块
模块允许您通过分组路由来组织应用程序。
中的 install 函数。 下面的代码片段展示了如何安装 Authentication ...

  • ... 在 embeddedServer 函数调用内部。
  • ... 在显式定义的 module 内部,它是 Application 类的一个扩展函数。
kotlin
kotlin

配置 Authentication

安装 Authentication 后,您可以按如下方式配置和使用 Authentication

步骤 1:选择认证提供者

要使用特定的认证提供者,例如 basicdigestform, 您需要在 install 代码块中调用相应的函数。例如,要使用 basic 认证, 请调用 .basic() 函数:

kotlin
import io.ktor.server.application.*
import io.ktor.server.auth.*
// ...
install(Authentication) {
    basic {
        // Configure basic authentication
    }
}

在此函数中,您可以 配置 此提供者特有的设置。

步骤 2:指定提供者名称

用于 使用特定提供者 的函数可选地允许您指定提供者名称。下面的代码 示例安装了 basic 和 form 提供者,分别命名为 "auth-basic""auth-form"

kotlin
install(Authentication) {
    basic("auth-basic") {
        // Configure basic authentication
    }
    form("auth-form") {
        // Configure form authentication
    }
    // ...
}

这些名称稍后可以使用来 认证不同的路由,使用不同的提供者。

请注意,提供者名称应该是唯一的,并且您只能定义一个没有名称的提供者。

步骤 3:配置提供者

每种 提供者类型 都有自己的配置。例如, BasicAuthenticationProvider.Config 类为 .basic() 函数提供选项。此类中的关键函数是 validate(), 它负责校验用户名和密码。以下代码示例演示了其用法:

kotlin
install(Authentication) {
    basic("auth-basic") {
        realm = "Access to the '/' path"
        validate { credentials ->
            if (credentials.name == "jetbrains" && credentials.password == "foobar") {
                UserIdPrincipal(credentials.name)
            } else {
                null
            }
        }
    }
}

要理解 validate() 函数的工作原理,我们需要介绍两个术语:

  • principal 是一种可以被认证的实体:用户、计算机、服务等。在 Ktor 中,各种认证提供者可能会使用不同的 principal。例如,basicdigestform 提供者认证 UserIdPrincipal, 而 jwt 提供者验证 JWTPrincipal

    您也可以创建自定义 principal。这在以下情况下可能很有用:

    • 将凭据映射到自定义 principal 允许您在 路由处理器 内部获取已认证 principal 的额外信息。
    • 如果您使用 session 认证,principal 可能是一个存储 session 数据的数据类。
  • credential 是一组服务器用于认证 principal 的属性:用户名/密码对、API 密钥等。例如,basicform 提供者使用 UserPasswordCredential 校验用户名和密码,而 jwt 校验 JWTCredential

因此,validate() 函数会检测指定的凭据并在认证成功的情况下返回一个 Any 类型的 principal,或在认证失败时返回 null

要根据特定条件跳过认证, 请使用 skipWhen()。 例如,如果 session 已存在,您可以跳过 basic 认证:

kotlin
basic {
    skipWhen { call -> call.sessions.get<UserSession>() != null }
}

步骤 4:保护特定资源

最后一步是保护应用程序中的特定资源。您可以使用 authenticate() 函数来实现。此函数接受两个可选形参:

  • 用于认证嵌套路由的 提供者名称。 下面的代码片段使用名为 auth-basic 的提供者来保护 /login/orders 路由:

    kotlin
    routing {
        authenticate("auth-basic") {
            get("/login") {
                // ...
            }    
            get("/orders") {
                // ...
            }    
        }
        get("/") {
            // ...
        }
    }
  • 用于解析嵌套认证提供者的策略。 此策略由 AuthenticationStrategy 枚举值表示。

    例如,客户端应为所有使用 AuthenticationStrategy.Required 策略注册的提供者提供认证数据。 在下面的代码片段中,只有通过 session 认证 的用户才能尝试使用 basic 认证访问 /admin 路由:

    kotlin
    routing {
        authenticate("auth-session", strategy = AuthenticationStrategy.Required) {
            get("/hello") {
                // ...
            }    
            authenticate("auth-basic", strategy = AuthenticationStrategy.Required) {
                get("/admin") {
                    // ...
                }
            }  
        }
    }

完整示例请参阅 auth-form-session-nested

步骤 5:在路由处理器内部获取 principal

认证成功后,您可以使用 call.principal() 函数在路由处理器内部检索已认证的 principal。此函数接受由 配置的认证提供者 返回的特定 principal 类型。在以下示例中,call.principal() 用于获取 UserIdPrincipal 并获取已认证用户的名称。

kotlin
routing {
    authenticate("auth-basic") {
        get("/") {
            call.respondText("Hello, ${call.principal<UserIdPrincipal>()?.name}!")
        }
    }
}

如果您使用 session 认证,principal 可能是一个存储 session 数据的数据类。 因此,您需要将此数据类传递给 call.principal()

kotlin
authenticate("auth-session") {
    get("/hello") {
        val userSession = call.principal<UserSession>()
    }
}

嵌套认证提供者 的情况下,您可以将 提供者名称 传递给 call.principal() 以获取所需提供者的 principal。

在下面的示例中,传递了 "auth-session" 值以获取顶层 session 提供者的 principal:

kotlin
authenticate("auth-session", strategy = AuthenticationStrategy.Required) {
    authenticate("auth-basic", strategy = AuthenticationStrategy.Required) {
        get("/admin") {
            val userSession = call.principal<UserSession>("auth-session")
        }
    }
}