Akka HTTP 401在未经身份验证的端点上

vbopmzt1  于 2022-11-05  发布在  其他
关注(0)|答案(1)|浏览(168)

我有以下路由定义

private val routes: Route =
    concat(
      pathEnd {
        get {
          handleErrorsAndReport("list_foos") {

          }
        }
      },
      path(Segment) { fooId =>
        get {
          handleErrorsAndReport("get_foo") {
            rejectEmptyResponse(
              complete(service.do(fooId))
            )
          }
        }
      },
      pathEnd {
        authenticateBasic(
          realm = "Secure scope",
          scopesAuthenticator
        ) { scopes =>
          post {
            handleErrorsAndReport("create_foo") {

            }
          }
        }
      },
      path(Segment) { fooId =>
        authenticateBasic(
          realm = "Secure scopes",
          scopesAuthenticator
        ) { scopes =>
          concat(
            put {
              handleErrorsAndReport("update_foo") {

              }
            },
            delete {
              handleErrorsAndReport("delete_foo") {

              }
            }
          )
        }
      }
    )

我正在尝试使用get_foo终结点。我已经为此创建了一个单元测试,它看起来像这样

"allow get operation on foo without authentication present" in {
      Get("/foos/{some_id}") ~> routes ~> check {
        status shouldBe StatusCodes.NotFound
      }
    }

在调试测试时,我可以看到路由被正确识别,并且我可以访问路由内的代码。get_foo路由内的服务代码生成None,complete(None)创建拒绝,因为它是一个空响应,并且我有rejectEmptyResponse指令。因此,我希望根据我定义的handleErrorsAndReport指令得到一个404响应。

private def handleErrorsAndReport(endpoint: String): Directive0 = extractRequestContext.flatMap { ctx =>
    val start = System.currentTimeMillis()
    mapResponse { resp =>
      // store response related metrics and return response
      resp
    } & handleExceptions(exceptionHandler)
  }

  private val exceptionHandler: ExceptionHandler = {

    def handle(e: Throwable, responseCode: StatusCode, errorMessage: Option[String]): Route = {
      extractRequest { request =>        

        val response = (responseCode, errorMessage) match {
          case (InternalServerError, _) => "Internal Server Error"
          case (_, Some(message)) => message
          case _ => "Bad Request"
        }

        complete(HttpResponse(responseCode, entity = response))
      }
    }

    ExceptionHandler {
      case e@AError(description) => handle(e, BadRequest, Some(description))
      case e: BError => handle(e, InternalServerError, Some(e.errorMessage))
      case e: CError => handle(e, BadRequest, Some(e.errorMessage))
      case e: DError => handle(e, BadRequest, Some(e.errorMessage))
      case e: EError => handle(e, BadRequest, Some(e.errorMessage))
      case e@FException(filter) => handle(e, BadRequest, Some(s"bla"))
      case other => handle(other, InternalServerError, Option(other.getMessage))
    }
  }

我得到的是一个401未经授权。这怎么可能呢?当我在调试代码时,我注意到控制流从来没有进入我的异常处理程序-我在里面到处添加了断点...

bn31dyow

bn31dyow1#

您的代码的问题在于,在拒绝了unauthenticated指令中的请求后,匹配发生在具有相同url的已验证路由上。因此,验证失败并导致401 unauthorized而不是404。
为了解决这个问题,您需要在使用GET方法的未经身份验证的路由失败后,通过将其 Package 在postputdelete路由内,使GET请求无法到达它,从而防止与经过身份验证的路由的这种匹配。
所以它可以写成

private val routes: Route =
    concat(
      pathEnd {
        get {
          handleErrorsAndReport("list_foos") {

          }
        }
      },
      path(Segment) { fooId =>
        get {
          handleErrorsAndReport("get_foo") {
            rejectEmptyResponse(
              complete(service.do(fooId))
            )
          }
        }
      },
      post {
         pathEnd {
          authenticateBasic(
            realm = "Secure scope",
            scopesAuthenticator
          ) { scopes =>

               handleErrorsAndReport("create_foo") {
               }

            }
         }
      },
      put{
         path(Segment) { fooId =>
           authenticateBasic(
           realm = "Secure scopes",
           scopesAuthenticator
          ) { scopes =>

              handleErrorsAndReport("update_foo") {

              }
            }
        }
      },
      delete{
         path(Segment) { fooId =>
           authenticateBasic(
           realm = "Secure scopes",
           scopesAuthenticator
          ) { scopes =>

              handleErrorsAndReport(“delete_foo") {

              }
           }
        }
      }
    )

相关问题