Autenticación de formulario en Ktor

Soy nuevo en Kotlin y Ktor tratando de ver la parte de authentication, así que obtuve el siguiente código.

Las routes "/" y "/ bye" funcionan bien, pero enrutan "login" a la página en blanco.

 package blog import kotlinx.html.* import kotlinx.html.stream.* // for createHTML import org.jetbrains.ktor.application.* import org.jetbrains.ktor.auth.* import org.jetbrains.ktor.features.* import org.jetbrains.ktor.http.* import org.jetbrains.ktor.response.* import org.jetbrains.ktor.routing.* import org.jetbrains.ktor.request.* // for request.uri import org.jetbrains.ktor.html.* import org.jetbrains.ktor.pipeline.* import org.jetbrains.ktor.host.* // for embededServer import org.jetbrains.ktor.netty.* // for Netty fun main(args: Array<String>) { embeddedServer(Netty, 8080, watchPaths = listOf("BlogAppKt"), module = Application::module).start() } fun Application.module() { install(DefaultHeaders) install(CallLogging) intercept(ApplicationCallPipeline.Call) { if (call.request.uri == "/hi") call.respondText("Test String") } install(Routing) { get("/") { call.respondText("""Hello, world!<br><a href="/bye">Say bye?</a>""", ContentType.Text.Html) } get("/bye") { call.respondText("""Good bye! <br><a href="/login">Login?</a> """, ContentType.Text.Html) } route("/login") { authentication { formAuthentication { up: UserPasswordCnetworkingential -> when { up.password == "ppp" -> UserIdPrincipal(up.name) else -> null } } } handle { val principal = call.authentication.principal<UserIdPrincipal>() if (principal != null) { call.respondText("Hello, ${principal.name}") } else { val html = createHTML().html { body { form(action = "/login", encType = FormEncType.applicationXWwwFormUrlEncoded, method = FormMethod.post) { p { +"user:" textInput(name = "user") { value = principal?.name ?: "" } } p { +"password:" passwordInput(name = "pass") } p { submitInput() { value = "Login" } } } } } call.respondText(html, ContentType.Text.Html) } } } } } 

Cuando desactivé la parte de authentication a continuación, la ruta '/ login' muestra el formulario requerido, lo que significa que el error es más probable en esta parte o en la forma de llamarlo. Supongo.

  authentication { formAuthentication { up: UserPasswordCnetworkingential -> when { up.password == "ppp" -> UserIdPrincipal(up.name) else -> null } } } 

No se le da simplemente una página en blanco, se obtiene un código de estado HTTP de 401 (UNAUTHORIZED) . Eso es porque formAuthentication tiene cuatro parameters, tres de ellos con valores pnetworkingeterminados. Solo implementó el último ( validate , sin valor pnetworkingeterminado):

 userParamName: String = "user", passwordParamName: String = "password", challenge: FormAuthChallenge = FormAuthChallenge.Unauthorized, validate: (UserPasswordCnetworkingential) -> Principal? 

Siempre que llegue a la ruta /login sin las cnetworkingenciales correctas que ya están en su lugar, obtendrá el valor pnetworkingeterminado para el challenge , que es FormAuthChallenge.Unauthorized , que es una respuesta 401 .

En lugar de utilizar el valor pnetworkingeterminado para el challenge , puede usar un FormAuthChallenge.Redirect . Un pequeño ejemplo que requiere dos routes:

 get("/login") { val html = """ <form action="/authenticate" enctype="..." REST OF YOUR LOGIN FORM </form> """ call.respondText(html, ContentType.Text.Html) } route("/authenticate") { authentication { formAuthentication(challenge = FormAuthChallenge.Redirect({ _, _ -> "/login" })) { cnetworkingential: UserPasswordCnetworkingential -> when { cnetworkingential.password == "secret" -> UserIdPrincipal(cnetworkingential.name) else -> null } } } handle { val principal = call.authentication.principal<UserIdPrincipal>() val html = "Hello, ${principal?.name}" call.respondText(html, ContentType.Text.Html) } } 

ACTUALIZAR

En caso de que lo anterior no haya funcionado bien, defina claramente tanto el userid-parameter como el password-parameter tal como aparecen en la form que hace el POST , como se muestra a continuación:

  authentication { formAuthentication("user", "pass", challenge = FormAuthChallenge.Redirect({ _, _ -> "/login" })){ cnetworkingential: UserPasswordCnetworkingential -> when { cnetworkingential.password == "secret" -> UserIdPrincipal(cnetworkingential.name) else -> null } } } 
  • Enviar request a otra URL de Ktor