Ktor supports authentication out of the box as a standard pluggable feature. It supports mechanisms to read credentials, and to authenticate principals.
It can be used in some cases along with the sessions feature to keep the login information between requests.
Table of contents:
io.ktor:ktor-auth:$ktor_version
中的
io.ktor.auth.Authentication
类中定义。
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 defines two concepts: credentials and principals.
To install it, you have to call to application.install(Authentication)
. You have to install this feature
directly to the application and it won’t work in another ApplicationCallPipeline
like Route
.
You might still be able to call the install code inside a Route if you have the Application injected in a nested DSL, but it will be applied to the application itself.
Using its DSL, it allows you to configure the authentication providers available:
install(Authentication) {
basic(name = "myauth1") {
realm = "Ktor Server"
validate { credentials ->
if (credentials.name == credentials.password) {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}
After defining one or more authentication providers (named or unnamed), with the routing feature you can create a route group, that will apply that authentication to all the routes defined in that group:
routing {
authenticate("myauth1") {
get("/authenticated/route1") {
// ...
}
get("/other/route2") {
// ...
}
}
get("/") {
// ...
}
}
You can specify several names to apply several authentication providers, or none or null to use the unnamed one.
You can get the generated Principal
instance inside your handler with:
val principal: UserIdPrincipal? = call.authentication.principal<UserIdPrincipal>()
In the generic, you have to put a specific type that must match the generated Principal. It will return null in the case you provide another type.
The handler won’t be executed if the configured authentication fails (when returning null
in the authentication mechanism)
It is possible to give arbitrary names to the authentication providers you specify, or to not provide a name at all (unnamed provider) by not setting the name argument or passing a null.
You cannot repeat authentication provider names, and you can define just one provider without a name.
In the case you repeat a name for the provider or try to define two unnamed providers, an exception will be thrown:
java.lang.IllegalArgumentException: Provider with the name `authName` is already registered
Summarizing:
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")
// ...
}
}
You can also skip an authentication based on a criteria.
/**
* 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)
For example, to skip a basic authentication if there is already a session, you could write:
authentication {
basic {
skipWhen { call -> call.sessions.get<UserSession>() != null }
}
}
If you want to create custom authentication strategies, you can check the Authentication feature as a reference.
The authentication feature defines two stages as part of its Pipeline: RequestAuthentication
and CheckAuthentication
.