【问题标题】:FOSRestBundle: Returning JSON/XML meta data for "Bad Credentials" ExceptionFOSRestBundle:为“坏凭证”异常返回 JSON/XML 元数据
【发布时间】:2013-11-07 12:37:36
【问题描述】:

我正在为我的 REST API 使用 FOSRestBundle,到目前为止,它一直是一个很棒的工具。我使用 HTTP Basic Auth 并且在大多数情况下它工作得很好。但是,当提交错误的凭据时,我遇到了捆绑包的异常行为问题。处理异常时(通过集成的身份验证处理程序或异常映射配置),捆绑包总是给我一个正确的 HTTP 状态和 JSON/XML 内容的响应,类似于:

{
   "code": 401,
   "message": "You are not authenticated"
}

这很好,当没有提交验证信息时也有效。但是,当提交 bad credentials(例如未知的用户名或不正确的密码)时,我会收到带有空消息正文的 HTTP 代码 401 Bad credentials(这很好)。相反,我会期待类似于上面的 JSON 的东西。

这是我这边的错误还是配置问题?我也很想知道捆绑包如何准确处理这些类型的身份验证错误,因为覆盖了BadCredentialsException 的状态捆绑包的异常配置部分的codes 部分中的代码似乎被忽略了。

谢谢!

【问题讨论】:

    标签: authentication symfony fosrestbundle


    【解决方案1】:

    好的,在深入研究了捆绑包的代码之后,我想通了。这个问题是由 Symfony 的 HTTP Basic Authentication impementation 处理错误凭据的方式造成的。 401 Bad Credentials 响应是由BasicAuthenticationEntryPoint 创建的自定义响应,由BasicAuthenticationListenerhandle 函数调用,在同一函数中抛出AuthenticationException 后立即调用。所以没有办法用监听器捕捉到这个异常:

    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();
    
        if (false === $username = $request->headers->get('PHP_AUTH_USER', false)) {
            return;
        }
    
        if (null !== $token = $this->securityContext->getToken()) {
            if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $username) {
                return;
            }
        }
    
        if (null !== $this->logger) {
            $this->logger->info(sprintf('Basic Authentication Authorization header found for user "%s"', $username));
        }
    
        try {
            $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
            $this->securityContext->setToken($token);
        } catch (AuthenticationException $failed) {
            $this->securityContext->setToken(null);
    
            if (null !== $this->logger) {
                $this->logger->info(sprintf('Authentication request failed for user "%s": %s', $username, $failed->getMessage()));
            }
    
            if ($this->ignoreFailure) {
                return;
            }
    
            $event->setResponse($this->authenticationEntryPoint->start($request, $failed));
        }
    }
    

    入口点的start 函数创建自定义响应,不涉及任何异常:

    public function start(Request $request, AuthenticationException $authException = null)
    {
        $response = new Response();
        $response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realmName));
        $response->setStatusCode(401, $authException ? $authException->getMessage() : null);
    
        return $response;
    }
    

    上面handle 函数中的第一个if-clause 也解释了为什么它在“根本没有用户凭据”的情况下工作,因为在这种情况下,侦听器只是停止尝试对用户进行身份验证,并且因此,Symfony 的防火墙侦听器会抛出异常(不太确定确切的位置),因此 FOSRestBundle 的 AccessDeniedListener 能够捕获 AuthenticationException 并执行其操作。

    【讨论】:

      【解决方案2】:

      您可以扩展AccessDeniedListener 并通过参数%fos_rest.access_denied_listener.class% 告诉FOSRestBundle 使用您自己的侦听器。 (service definition)

      parameters:
          fos_rest.access_denied_listener.class: Your\Namespace\For\AccessDeniedListener
      

      然后添加对BadCredentialsException 的额外检查,并使用所需的代码/消息发送HttpException,类似于Line 70 的AuthenticationException 检查。

      【讨论】:

      • 感谢您的快速回答!不幸的是,AccessDeniedListener 在“不良凭据”案例中似乎没有被调用。我的自定义 AccessDeniedListener 在“根本没有身份验证”的情况下被调用。但是,如果我在Line 74 添加一个else 路径(它应该捕获各种其他异常)并返回一些任意的HttpException,我仍然会收到一个401 Bad Credentials,以防PW 不正确。任何的想法?也许其他听众的优先级更高?
      • 您可以使用app/console container:debug --tag=kernel.event_listener --show-private 找到其他听众/订阅者 - 请参阅my answer here
      • 我在调试时正在寻找这个,谢谢!幸运的是,我发现了问题,这与 Symfony 的 HTTP 基本身份验证侦听器有关(请参阅我的答案)。
      猜你喜欢
      • 2011-02-21
      • 2022-01-07
      • 1970-01-01
      • 1970-01-01
      • 2020-01-26
      • 1970-01-01
      • 2019-04-09
      • 2015-04-21
      • 2015-05-30
      相关资源
      最近更新 更多