Как извлечь валидацию прав доступа в Kotlin's Ktor - PullRequest
1 голос
/ 25 мая 2019

У меня есть приложение REST API на основе Ktor, которое использует токен 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)
  }
}

А затем подключите его к остальным конечным точкам. Или есть какой-то похожий подход, который я могу использовать в Ktor Kotlin? Кажется, мне нужны перехватчики, но я не уверен, как использовать их по назначению.

1 Ответ

1 голос
/ 29 мая 2019

Вы можете проверить метод и URI в блоке validate.

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)
    }
}

Кстати, с кодом, который вы разместили, было несколько проблем, поэтому он не скомпилировался.

...