如何在 Kotlin 的 Ktor 中提取访问权限验证
How to extract access rights validation in Kotlin's Ktor
我有基于 Ktor 的 REST API 应用程序,它使用 jwt
令牌作为身份验证。然后我必须限制特定角色的某些路由。为此,我正在创建主体,其中包含相关信息:
data class UserPrincipal (
val id: Long,
val username: String,
val roleId: Long,
): Princpal {
override fun getName() = username
}
object AuthLogin {
fun Application.auth(jwt: JwtProvider) {
install(Authentication) {
jwt("jwt") {
realm = jwt.realm()
verifier(jwt.verifier())
validate {
val userId = it.payload.getClaim("id").asLong()
val username = it.payload.getClain("name")
val roleId = it.payload.getClaim("roleId").asLong()
UserPrincipal(userId, username, roleId)
}
}
}
}
}
在对正确登录的用户进行签名时,将提供带有 userId
和 roleId
的声明。现在我可以像这样限制 REST 端点:
object RestModule {
fun Application.enititiesOne(userRepo: UserRepo) {
routing {
authenticate("jwt") {
route("/entities1") {
get {
val principal = call.principal<UserPrincipal>()
when(userRepo.hasAccessByRole(principal!!.roleId, "CAN_R_E1") {
false -> call.respond(HttpStatusCode.Forbidden)
true -> // some retrieval logic
}
post {
val principal = call.principal<UserPrincipal>()
when(userRepo.hasAccessByRole(principal!!.roleId, "CAN_W_E1") {
false -> call.respond(HttpStatusCode.Forbidden)
true -> // some update logic
}
}
}
}
}
}
如您所见,即使在一个路由函数中,我也必须复制两次检查委托人角色的代码。我可以将其移出运行,但我想要的是在一个地方定义我的安全角色。类似的东西:
authenticate {
val principal = call.principal<UserPrincipal()
val rights = userRepo.rightsByRole(principal.roleId)
when(routes) {
get("/entities1/**") ->
if(rights.contain("CAN_R_E1")) call.proceed
else call.respond(HttpStatusCode.Forbidden)
post("/entites1) -> rights.contain("CAN_W_E1") // similar
get("/entities2/**") -> rights.contain("CAN_R_E2") // similar
else -> call.respond(401)
}
}
然后将其插入其余端点。或者我可以在 Kotlin 的 Ktor 中使用一些类似的方法吗?似乎拦截器是我需要的,但我不确定如何以预期的方式使用它们。
您可以在validate
块中查看方法和uri。
install(Authentication) {
jwt {
validate {
val userId = it.payload.getClaim("id").asLong()
val username = it.payload.getClaim("name").asString()
val roleId = it.payload.getClaim("roleId").asLong()
UserPrincipal(userId, username, roleId)
val requiredRole = when (request.httpMethod) {
HttpMethod.Get -> // get role
HttpMethod.Post -> // get other role
}
// check if role exists in repo
}
}
}
install(Routing) {
get {
val principal = call.principal<UserPrincipal>()!!
call.respond(principal)
}
post {
val principal = call.principal<UserPrincipal>()!!
call.respond(principal)
}
}
顺便说一句,您发布的代码存在几个问题,因此无法编译。
我有基于 Ktor 的 REST API 应用程序,它使用 jwt
令牌作为身份验证。然后我必须限制特定角色的某些路由。为此,我正在创建主体,其中包含相关信息:
data class UserPrincipal (
val id: Long,
val username: String,
val roleId: Long,
): Princpal {
override fun getName() = username
}
object AuthLogin {
fun Application.auth(jwt: JwtProvider) {
install(Authentication) {
jwt("jwt") {
realm = jwt.realm()
verifier(jwt.verifier())
validate {
val userId = it.payload.getClaim("id").asLong()
val username = it.payload.getClain("name")
val roleId = it.payload.getClaim("roleId").asLong()
UserPrincipal(userId, username, roleId)
}
}
}
}
}
在对正确登录的用户进行签名时,将提供带有 userId
和 roleId
的声明。现在我可以像这样限制 REST 端点:
object RestModule {
fun Application.enititiesOne(userRepo: UserRepo) {
routing {
authenticate("jwt") {
route("/entities1") {
get {
val principal = call.principal<UserPrincipal>()
when(userRepo.hasAccessByRole(principal!!.roleId, "CAN_R_E1") {
false -> call.respond(HttpStatusCode.Forbidden)
true -> // some retrieval logic
}
post {
val principal = call.principal<UserPrincipal>()
when(userRepo.hasAccessByRole(principal!!.roleId, "CAN_W_E1") {
false -> call.respond(HttpStatusCode.Forbidden)
true -> // some update logic
}
}
}
}
}
}
如您所见,即使在一个路由函数中,我也必须复制两次检查委托人角色的代码。我可以将其移出运行,但我想要的是在一个地方定义我的安全角色。类似的东西:
authenticate {
val principal = call.principal<UserPrincipal()
val rights = userRepo.rightsByRole(principal.roleId)
when(routes) {
get("/entities1/**") ->
if(rights.contain("CAN_R_E1")) call.proceed
else call.respond(HttpStatusCode.Forbidden)
post("/entites1) -> rights.contain("CAN_W_E1") // similar
get("/entities2/**") -> rights.contain("CAN_R_E2") // similar
else -> call.respond(401)
}
}
然后将其插入其余端点。或者我可以在 Kotlin 的 Ktor 中使用一些类似的方法吗?似乎拦截器是我需要的,但我不确定如何以预期的方式使用它们。
您可以在validate
块中查看方法和uri。
install(Authentication) {
jwt {
validate {
val userId = it.payload.getClaim("id").asLong()
val username = it.payload.getClaim("name").asString()
val roleId = it.payload.getClaim("roleId").asLong()
UserPrincipal(userId, username, roleId)
val requiredRole = when (request.httpMethod) {
HttpMethod.Get -> // get role
HttpMethod.Post -> // get other role
}
// check if role exists in repo
}
}
}
install(Routing) {
get {
val principal = call.principal<UserPrincipal>()!!
call.respond(principal)
}
post {
val principal = call.principal<UserPrincipal>()!!
call.respond(principal)
}
}
顺便说一句,您发布的代码存在几个问题,因此无法编译。