クライアントの認証

Estimated reading time: 2 minutes

Ktorは標準のプラグイン可能なFeatureとして、すぐに使える認証機能をサポートしています。 credentialの読み込みとprincipalの認証のメカニズムをサポートしています。

リクエスト間でログイン情報を保持するためにsession機能とともに使われるケースもあります。

目次:

This feature is defined in the class io.ktor.auth.Authentication in the artifact io.ktor:ktor-auth:$ktor_version.
dependencies { implementation "io.ktor:ktor-auth:$ktor_version" }
dependencies { implementation("io.ktor:ktor-auth:$ktor_version") }
<project> ... <dependencies> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-auth</artifactId> <version>${ktor.version}</version> <scope>compile</scope> </dependency> </dependencies> </project>

メカニズム

基本的な使い方

Ktorはcredentialとprincipalという2つの概念を定義しています。

  • principalは認証されている何かを指します。ユーザやコンピュータやグループなどが例です。
  • credentialは、principalを認証するサーバーに提示するプロパティのセットです。user/password、APIキー、authenticated payload signatureなどが例です。

インストールするためには、application.install(Authentication)を呼び出す必要があります。 applicationに対し直接インストールする必要があり、Routeのような別のApplicationCallPipeline内では動きません。

Routeの内側でインストールするコードを実行することができますが、適用されるのはapplicationそのものに対してです。

DSLを使うことで、認証プロバイダの設定を可能にします:

install(Authentication) {
    basic(name = "myauth1") {
        realm = "Ktor Server"
        validate { credentials ->
            if (credentials.name == credentials.password) {
                UserIdPrincipal(credentials.name)
            } else {
                null
            }
        }
    }
}

1つ以上の認証プロバイダを(名前付きでも名前無しでも)定義した後、routing機能を使って、 定義した認証が適用されるようなrouteのグループを作成できます。

routing {
    authenticate("myauth1") {
        get("/authenticated/route1") {
            // ...
        }    
        get("/other/route2") {
            // ...
        }    
    }
    get("/") {
        // ...
    }
}

いくつかの認証プロバイダを適用するために名前を指定することもできますし、あるいは名前を指定せず使うこともできます。

生成されたPrincipalのインスタンスをhandlerの内側で以下のように取得することができます:

val principal: UserIdPrincipal? = call.authentication.principal<UserIdPrincipal>()

一般的には、生成したPrincipalにマッチする必要がある特定の型を指定する必要があります。 別の型を指定した場合、nullが返ります。

設定された認証が失敗した場合handlerは実行されません(認証メカニズム内でnullが返ったとき)

AuthenticationProviderに名前付け

指定の認証プロバイダに任意の名前をつけることができますし、あるいは名前の引数を指定しないまたはnullを渡すことで名前をつけないこともできます。

認証プロバイダ名を重複させることはできず、また名前なしのプロバイダは1つだけしか定義できません。

プロバイダの名前を重複させた場合や、2つの未命名のプロバイダがあった場合は、exceptionが投げられます:

java.lang.IllegalArgumentException: Provider with the name `authName` is already registered

要約すると以下のようになります:

install(Authentication) {
    basic { // Unamed `basic` provider
        // ...
    }
    form { // Unamed `form` provider (exception, already defined a provider with name = null) 
        // ...
    }
    basic("name1") { // "name1" provider
        // ...
    }
    basic("name1") { // "name1" provider (exception, already defined a provider with name = "name1")
        // ...
    }
}

AuthenticationProviderのスキップ

基準にしたがって認証をスキップさせることもできます。

/**
 * Authentication filters specifying if authentication is required for particular [ApplicationCall]
 * If there is no filters, authentication is required. If any filter returns true, authentication is not required.
 */
fun AuthenticationProvider.skipWhen(predicate: (ApplicationCall) -> Boolean)

例えば、すでにセッションがある場合にbasic認証をスキップさせるためには、以下のように書けます:

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

発展的内容

カスタムの認証を行いたい場合は、Authentication機能のページを参照してください。

認証機能はPipelineにおいてRequestAuthenticationCheckAuthenticationという2つのステージで定義されています