Ktor Serverにおける認証と認可
必要な依存関係: io.ktor:ktor-server-auth
Ktorは、認証と認可を処理するためにAuthenticationプラグインを提供します。典型的な使用シナリオには、ユーザーのログイン、特定のリソースへのアクセス許可、関係者間での情報の安全な送信などがあります。Authentication
をセッションと併用して、リクエスト間でユーザー情報を保持することもできます。
クライアント側では、Ktorは認証と認可を処理するためのAuthenticationプラグインを提供します。
サポートされている認証タイプ
Ktorは以下の認証および認可スキームをサポートしています。
HTTP認証
HTTPは、アクセス制御と認証のための一般的なフレームワークを提供します。Ktorでは、以下のHTTP認証スキームを使用できます。
- Basic -
Base64
エンコーディングを使用してユーザー名とパスワードを提供します。HTTPSと組み合わせて使用しない限り、一般的に推奨されません。 - Digest - ユーザー名とパスワードにハッシュ関数を適用することで、ユーザー資格情報を暗号化された形式で通信する認証方法です。
- Bearer - Bearerトークンと呼ばれるセキュリティトークンを使用する認証スキームです。 Bearer認証スキームはOAuthまたはJWTの一部として使用されますが、Bearerトークンを認可するためのカスタムロジックを提供することもできます。
フォームベース認証
フォームベース認証は、ウェブフォームを使用して資格情報を収集し、ユーザーを認証します。
JSON Webトークン (JWT)
JSON Webトークンは、JSONオブジェクトとして関係者間で情報を安全に送信するためのオープンスタンダードです。JSON Webトークンを認可に使用できます。ユーザーがログインすると、各リクエストにはトークンが含まれ、そのトークンで許可されたリソースにユーザーがアクセスできるようになります。Ktorでは、jwt
認証を使用してトークンを検証し、その中に含まれるクレームを検証できます。
LDAP
LDAPは、ディレクトリサービス認証に使用されるオープンなクロスプラットフォームプロトコルです。Ktorは、指定されたLDAPサーバーに対してユーザー資格情報を認証するために、ldapAuthenticate関数を提供します。
OAuth
OAuthは、APIへのアクセスを保護するためのオープンスタンダードです。Ktorのoauth
プロバイダーを使用すると、Google、Facebook、Twitterなどの外部プロバイダーを使用して認証を実装できます。
セッション
セッションは、異なるHTTPリクエスト間でデータを永続化するメカニズムを提供します。典型的な使用例としては、ログインしたユーザーのID、ショッピングバスケットの内容の保存、クライアント上でのユーザー設定の保持などがあります。Ktorでは、関連付けられたセッションを持つユーザーは、session
プロバイダーを使用して認証できます。その方法はKtor Serverでのセッション認証で学ぶことができます。
カスタム
Ktorはカスタムプラグインを作成するためのAPIも提供しており、認証と認可を処理するための独自のプラグインを実装するために使用できます。 例えば、AuthenticationChecked
フックは認証資格情報がチェックされた後に実行され、認可を実装することができます: custom-plugin-authorization。
依存関係の追加
Authentication
を使用するには、ビルドスクリプトにktor-server-auth
アーティファクトを含める必要があります:
JWTやLDAPなど、一部の認証プロバイダーは追加のアーティファクトを必要とすることに注意してください。
Authenticationのインストール
アプリケーションにAuthentication
プラグインをインストールするには、指定された
install
関数に渡します。以下のコードスニペットは、Authentication
をインストールする方法を示しています... - ...
embeddedServer
関数呼び出し内で。 - ...
Application
クラスの拡張関数である明示的に定義されたmodule
内で。
Authenticationの設定
Authenticationをインストールした後、次のようにAuthentication
を設定して使用できます。
ステップ1: 認証プロバイダーを選択する
basic、digest、formなどの特定の認証プロバイダーを使用するには、install
ブロック内で対応する関数を呼び出す必要があります。例えば、basic認証を使用するには、.basic()
関数を呼び出します。
import io.ktor.server.application.*
import io.ktor.server.auth.*
// ...
install(Authentication) {
basic {
// Configure basic authentication
}
}
この関数内で、このプロバイダーに固有の設定を構成できます。
ステップ2: プロバイダー名を指定する
特定のプロバイダーを使用するための関数は、オプションでプロバイダー名を指定できます。以下のコードサンプルは、basicおよびformプロバイダーをそれぞれ"auth-basic"
および"auth-form"
という名前でインストールしています。
install(Authentication) {
basic("auth-basic") {
// Configure basic authentication
}
form("auth-form") {
// Configure form authentication
}
// ...
}
これらの名前は後で異なるプロバイダーを使用して異なるルートを認証するために使用できます。
プロバイダー名は一意である必要があり、名前なしのプロバイダーは1つしか定義できないことに注意してください。
ステップ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()
関数の動作を理解するには、2つの用語を導入する必要があります。
- プリンシパル(principal)とは、認証できるエンティティです。ユーザー、コンピューター、サービスなどです。Ktorでは、さまざまな認証プロバイダーが異なるプリンシパルを使用する場合があります。例えば、
basic
、digest
、form
プロバイダーはUserIdPrincipal
を認証し、jwt
プロバイダーはJWTPrincipal
を検証します。カスタムプリンシパルを作成することもできます。これは次の場合に役立つ可能性があります。
- クレデンシャル(credential)とは、サーバーがプリンシパルを認証するための一連のプロパティです。ユーザー名とパスワードのペア、APIキーなどです。例えば、
basic
およびform
プロバイダーはUserPasswordCredential
を使用してユーザー名とパスワードを検証し、jwt
はJWTCredential
を検証します。
したがって、validate()
関数は指定されたクレデンシャルをチェックし、認証が成功した場合はプリンシパルAny
を返し、認証が失敗した場合はnull
を返します。
特定の基準に基づいて認証をスキップするには、
skipWhen()
を使用します。例えば、セッションが既に存在する場合、basic
認証をスキップできます。kotlinbasic { skipWhen { call -> call.sessions.get<UserSession>() != null } }
ステップ4: 特定のリソースを保護する
最後のステップは、アプリケーション内の特定のリソースを保護することです。これはauthenticate()
関数を使用することで実現できます。この関数は2つのオプションパラメーターを受け入れます。
ネストされたルートを認証するために使用されるプロバイダーの名前。以下のコードスニペットは、_auth-basic_という名前のプロバイダーを使用して
/login
および/orders
ルートを保護しています。kotlinrouting { authenticate("auth-basic") { get("/login") { // ... } get("/orders") { // ... } } get("/") { // ... } }
ネストされた認証プロバイダーを解決するために使用される戦略。この戦略は
AuthenticationStrategy
列挙値で表されます。例えば、クライアントは
AuthenticationStrategy.Required
戦略で登録されたすべてのプロバイダーに対して認証データを提供する必要があります。 以下のコードスニペットでは、セッション認証を通過したユーザーのみがbasic認証を使用して/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"
値が渡されています。
authenticate("auth-session", strategy = AuthenticationStrategy.Required) {
authenticate("auth-basic", strategy = AuthenticationStrategy.Required) {
get("/admin") {
val userSession = call.principal<UserSession>("auth-session")
}
}
}