Ktor 伺服器中的認證與授權
所需相依性:io.ktor:ktor-server-auth
Ktor 提供 Authentication 外掛程式來處理認證與授權。典型的使用情境包括登入使用者、授予對特定資源的存取權限以及在各方之間安全地傳輸資訊。您也可以將 Authentication
與 Sessions 搭配使用,以在請求之間保留使用者的資訊。
在客戶端,Ktor 提供 Authentication 外掛程式來處理認證與授權。
支援的認證類型
Ktor 支援以下認證與授權方案:
HTTP 認證
HTTP 提供了一個用於存取控制與認證的通用框架。在 Ktor 中,您可以使用以下 HTTP 認證方案:
- Basic - 使用
Base64
編碼提供使用者名稱與密碼。如果未與 HTTPS 結合使用,通常不建議。 - Digest - 一種認證方法,透過對使用者名稱和密碼應用雜湊函數,以加密形式傳輸使用者憑證。
- Bearer - 一種涉及稱為 Bearer token 的安全令牌的認證方案。 Bearer 認證方案作為 OAuth 或 JWT 的一部分使用,但您也可以提供自訂邏輯來授權 Bearer token。
基於表單的認證
JSON Web Token (JWT)
JSON Web Token 是一種用於在各方之間安全傳輸資訊的開放標準,以 JSON 物件形式呈現。您可以將 JSON Web Token 用於授權:當使用者登入後,每個請求都將包含一個 token,允許使用者存取該 token 允許的資源。在 Ktor 中,您可以使用 jwt
認證來驗證 token 並驗證其中包含的 claims。
LDAP
LDAP 是一種開放且跨平台的協議,用於目錄服務認證。Ktor 提供 ldapAuthenticate 函數,用於根據指定的 LDAP 伺服器驗證使用者憑證。
OAuth
OAuth 是一種用於保護 API 存取的開放標準。Ktor 中的 oauth
供應器允許您使用 Google、Facebook、Twitter 等外部供應器來實作認證。
會話
會話 提供一種機制,用於在不同 HTTP 請求之間持久化資料。典型的使用情境包括儲存已登入使用者的 ID、購物籃的內容或在客戶端保留使用者偏好設定。在 Ktor 中,已具有關聯會話的使用者可以使用 session
供應器進行認證。從 Ktor 伺服器中的會話認證了解如何執行此操作。
自訂
Ktor 還提供一個用於建立自訂外掛程式的 API,可用於實作您自己的外掛程式來處理認證與授權。 例如,AuthenticationChecked
鉤點 在認證憑證檢查後執行,它允許您實作授權:custom-plugin-authorization。
新增相依性
若要使用 Authentication
,您需要在建置腳本中包含 ktor-server-auth
artifact:
請注意,某些認證供應器,例如 JWT 和 LDAP,需要額外的 artifact。
安裝 Authentication
若要將 Authentication
外掛程式安裝到應用程式, 請在指定的
install
函數。 以下程式碼片段展示了如何安裝 Authentication
... - ... 在
embeddedServer
函數呼叫內部。 - ... 在明確定義的
module
內部,該module
是Application
類別的擴展函數。
設定 Authentication
在安裝 Authentication 之後,您可以如下設定和使用 Authentication
:
步驟 1:選擇認證供應器
若要使用特定的認證 供應器,例如 basic、digest 或 form, 您需要在 install
區塊內呼叫對應的函數。例如,要使用基本認證, 請呼叫 .basic()
函數:
import io.ktor.server.application.*
import io.ktor.server.auth.*
// ...
install(Authentication) {
basic {
// Configure basic authentication
}
}
在此函數內部,您可以設定此供應器特有的設定。
步驟 2:指定供應器名稱
使用特定供應器的函數允許您選擇性地指定供應器名稱。下面的程式碼範例分別使用 "auth-basic"
和 "auth-form"
名稱安裝 basic 和 form 供應器:
install(Authentication) {
basic("auth-basic") {
// Configure basic authentication
}
form("auth-form") {
// Configure form authentication
}
// ...
}
這些名稱稍後可用於使用不同供應器驗證不同的路由。
請注意,供應器名稱應該是唯一的,並且您只能定義一個沒有名稱的供應器。
步驟 3:設定供應器
每個供應器類型都有其自己的設定。例如, BasicAuthenticationProvider.Config
類別為 .basic()
函數提供了選項。此類別中的關鍵函數是 validate()
, 它負責驗證使用者名稱和密碼。以下程式碼範例展示了其用法:
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()
函數如何運作,我們需要介紹兩個術語:
- 主體 是一個可以被認證的實體:使用者、電腦、服務等。在 Ktor 中,各種 認證供應器可能會使用不同的主體。例如,
basic
、digest
和form
供應器 認證UserIdPrincipal
, 而jwt
供應器 驗證JWTPrincipal
。您也可以建立自訂主體。這在以下情況中可能很有用:
- 憑證 是一組用於伺服器認證主體的屬性:使用者/密碼對、API 金鑰等等。例如,
basic
和form
供應器 使用UserPasswordCredential
來驗證使用者名稱和密碼,而jwt
驗證JWTCredential
。
因此,validate()
函數檢查指定的憑證,並在認證成功時傳回一個 Any
主體,如果認證失敗則傳回 null
。
若要根據特定條件跳過認證, 請使用
skipWhen()
。 例如,如果會話已存在,您可以跳過basic
認證:kotlinbasic { skipWhen { call -> call.sessions.get<UserSession>() != null } }
步驟 4:保護特定資源
最後一步是保護應用程式中的特定資源。您可以使用 authenticate()
函數來完成此操作。此函數接受兩個可選參數:
用於認證巢狀路由的供應器名稱。 以下程式碼片段使用名為 auth-basic 的供應器來保護
/login
和/orders
路由:kotlinrouting { authenticate("auth-basic") { get("/login") { // ... } get("/orders") { // ... } } get("/") { // ... } }
用於解析巢狀認證供應器的策略。 此策略由
AuthenticationStrategy
列舉值表示。例如,客戶端應為所有使用
AuthenticationStrategy.Required
策略註冊的供應器提供認證資料。 在以下程式碼片段中,只有通過會話認證的使用者 才能嘗試使用基本認證存取/admin
路由:kotlinrouting { authenticate("auth-session", strategy = AuthenticationStrategy.Required) { get("/hello") { // ... } authenticate("auth-basic", strategy = AuthenticationStrategy.Required) { get("/admin") { // ... } } } }
有關完整範例,請參閱 auth-form-session-nested。
步驟 5:在路由處理器內部取得主體
認證成功後,您可以使用 call.principal()
函數在路由處理器內部檢索已認證的主體。此函數接受由 已設定的認證供應器傳回的特定主體類型。在以下範例中, call.principal()
用於取得 UserIdPrincipal
並獲取已認證使用者的名稱。
routing {
authenticate("auth-basic") {
get("/") {
call.respondText("Hello, ${call.principal<UserIdPrincipal>()?.name}!")
}
}
}
如果您使用會話認證,主體可能是一個儲存會話資料的資料類別。 因此,您需要將此資料類別傳遞給 call.principal()
:
authenticate("auth-session") {
get("/hello") {
val userSession = call.principal<UserSession>()
}
}
在巢狀認證供應器的情況下, 您可以將供應器名稱傳遞給 call.principal()
以取得所需供應器的主體。
在下面的範例中,將 "auth-session"
值傳遞給 call.principal()
以取得最上層會話供應器的主體:
authenticate("auth-session", strategy = AuthenticationStrategy.Required) {
authenticate("auth-basic", strategy = AuthenticationStrategy.Required) {
get("/admin") {
val userSession = call.principal<UserSession>("auth-session")
}
}
}