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 token 的安全令牌的認證方案。 Bearer 認證方案作為 OAuthJWT 的一部分使用,但您也可以提供自訂邏輯來授權 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:

Kotlin
Groovy
XML

請注意,某些認證供應器,例如 JWTLDAP,需要額外的 artifact。

安裝 Authentication

若要將 Authentication 外掛程式安裝到應用程式, 請在指定的

模組
Modules allow you to structure your application by grouping routes.
中將其傳遞給 install 函數。 以下程式碼片段展示了如何安裝 Authentication ...

  • ... 在 embeddedServer 函數呼叫內部。
  • ... 在明確定義的 module 內部,該 moduleApplication 類別的擴展函數。
kotlin
kotlin

設定 Authentication

安裝 Authentication 之後,您可以如下設定和使用 Authentication

步驟 1:選擇認證供應器

若要使用特定的認證 供應器,例如 basicdigestform, 您需要在 install 區塊內呼叫對應的函數。例如,要使用基本認證, 請呼叫 .basic() 函數:

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

在此函數內部,您可以設定此供應器特有的設定。

步驟 2:指定供應器名稱

使用特定供應器的函數允許您選擇性地指定供應器名稱。下面的程式碼範例分別使用 "auth-basic""auth-form" 名稱安裝 basicform 供應器:

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() 函數如何運作,我們需要介紹兩個術語:

  • 主體 是一個可以被認證的實體:使用者、電腦、服務等。在 Ktor 中,各種 認證供應器可能會使用不同的主體。例如,basicdigestform 供應器 認證 UserIdPrincipal, 而 jwt 供應器 驗證 JWTPrincipal

    您也可以建立自訂主體。這在以下情況中可能很有用:

    • 將憑證映射到自訂主體允許您在路由處理器內部擁有關於已認證主體的額外資訊。
    • 如果您使用會話認證,主體可能是一個儲存會話資料的資料類別。
  • 憑證 是一組用於伺服器認證主體的屬性:使用者/密碼對、API 金鑰等等。例如,basicform 供應器 使用 UserPasswordCredential 來驗證使用者名稱和密碼,而 jwt 驗證 JWTCredential

因此,validate() 函數檢查指定的憑證,並在認證成功時傳回一個 Any 主體,如果認證失敗則傳回 null

若要根據特定條件跳過認證, 請使用 skipWhen()。 例如,如果會話已存在,您可以跳過 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 策略註冊的供應器提供認證資料。 在以下程式碼片段中,只有通過會話認證的使用者 才能嘗試使用基本認證存取 /admin 路由:

    kotlin
    routing {
        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 並獲取已認證使用者的名稱。

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

如果您使用會話認證,主體可能是一個儲存會話資料的資料類別。 因此,您需要將此資料類別傳遞給 call.principal()

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

巢狀認證供應器的情況下, 您可以將供應器名稱傳遞給 call.principal() 以取得所需供應器的主體。

在下面的範例中,將 "auth-session" 值傳遞給 call.principal() 以取得最上層會話供應器的主體:

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