Ktor:如何在路由处理程序中检查身份验证?

Ktor: How to check authentication inside a route handler?

我正在使用 ktor v0.9.2,我想根据用户是否经过身份验证为同一路由发送不同的内容。

我遇到的问题是我无法访问 authenticate { } 块之外的主体。

我的设置是这样的:

data class User(
    val userId: Int
) : io.ktor.auth.Principal

fun Application.myApp() {
    install(Authentication) {
        jwt {
            verifier(JwtConfig.verifier)
            validate { credential ->
                val userId = credential.payload.getClaim("userId").asInt()
                when {
                    userId > 0 -> User(userId)
                    else -> null
                }
            }
        }
    }
    install(DefaultHeaders)
    install(CallLogging)
    install(ContentNegotiation) {
        jackson { }
    }
    install(Routing) {
        authenticate {
            get("/protected") {
                // This works fine!!
                val user = call.authentication.principal<User>()
                call.respond(user)
            }
        }

        get("/") {
            val user = call.authentication.principal<User>() // -> User is always null here
            if (user == null) {
                call.respondText("Not Logged User")
            } else {
                call.respondText("Logged User")
            }
        }
    }
}

/protected 路由工作正常,但在 / 路由中主体始终为空。我认为这是一些管道问题,但我有点迷路。有人可以提供一些见解吗?谢谢!

您使用的是什么版本的 ktor? 你能告诉我们你的身份验证设置吗?

你应该有这样的东西(0.9.2):

install(Authentication) {
        jwt {
            verifier(JwtConfig.verifier)
            realm = JwtConfig.realm
            validate {
                val email = it.payload.getClaim("email").toString()
                userRepository.findUser(email)?.let { user ->
                    val token = JwtConfig.makeToken(user)
                    user.copy(token = token)
                }
            }
        }

}

如果身份验证过程成功,用户将可以通过主体访问。

这是 0.9.3 的更新代码。 从验证行为的测试开始。 只需添加 optional 标志。

class KtorTest {

    @Test fun server() {
        withTestApplication({ myApp() }) {
            val userId = 918354853
            val token = JwtConfig.makeToken(User(userId))
            // The protected route
            handleRequest {
                uri = "/protected"
                addHeader("Authorization", "Bearer $token")
            }.let {
                it.requestHandled shouldBe true
                it.response.content.shouldNotBeNullOrBlank() shouldContain userId.toString()
            }

            // Optional route without token
            handleRequest {}.let {
                it.requestHandled shouldBe true
                it.response.content.shouldNotBeNullOrBlank() shouldBeEqualTo "Not Logged User"
            }

            // Optional route with token
            handleRequest {
                addHeader("Authorization", "Bearer $token")
            }.let {
                it.requestHandled shouldBe true
                it.response.content.shouldNotBeNullOrBlank() shouldBeEqualTo "Logged User"
            }
        }
    }

}

data class User(val userId: Int) : Principal

fun Application.myApp() {
    install(Authentication) {
        jwt {
            verifier(JwtConfig.verifier)
            validate { credential ->
                val userId = credential.payload.getClaim("userId").asInt()
                when {
                    userId > 0 -> User(userId)
                    else -> null
                }
            }
        }
    }
    install(DefaultHeaders)
    install(CallLogging)
    install(ContentNegotiation) { jackson { } }
    install(Routing) {
        authenticate {
            get("/protected") {
                // This works fine!!
                val user = call.authentication.principal<User>()!!
                call.respond(user)
            }
        }

        authenticate(optional = true) {
            get("/") {
                val user = call.authentication.principal<User>() // -> User is always null here
                if (user == null) {
                    call.respondText("Not Logged User")
                } else {
                    call.respondText("Logged User")
                }
            }
        }
    }
}