Symfony guard:access_control无效

时间:2017-03-03 10:38:39

标签: php symfony

我是symfony的新手,我正在尝试使用基于JWT的基本防护验证器。这项工作主要来自这里的文章,我已经删除了任何用户检查(暂时):http://kolabdigital.com/lab-time/symfony-json-web-tokens-authentication-guard

我觉得有些东西是我无法得到的,因为我无法让它发挥作用。更准确地说,即使在我实施的例外情况下,它也可以在任何地方使用。

这是Check服务,与文章基本相同,没有用户管理,并且有一些日志记录:

<?php

namespace AppBundle\Security;

use Lexik\Bundle\JWTAuthenticationBundle\Encoder\DefaultEncoder;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Psr\Log\LoggerInterface;

class TokenAuthenticator extends AbstractGuardAuthenticator
{
    private $jwtEncoder;
    private $logger;

    public function __construct(DefaultEncoder $jwtEncoder, LoggerInterface $logger)
    {
        $this->logger = $logger;
        $this->jwtEncoder = $jwtEncoder;
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        $route = $request->attributes->get('_route');
        $url = $request->getUri();
        $this->logger->info($route . ' : ' . $url);
        return new JsonResponse('Authentication required', 401);
    }

    public function getCredentials(Request $request)
    {

        if(!$request->headers->has('Authorization')) {
            return;
        }

        $extractor = new AuthorizationHeaderTokenExtractor(
            'Bearer',
            'Authorization'
        );

        $token = $extractor->extract($request);

        if(!$token) {
            return;
        }

        return $token;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $data = $this->jwtEncoder->decode($credentials);

        if(!$data){
            return;
        }

        $username = $data['username'];

        // TODO get user from user collection
        $user = ['username' => $username];

        // Is user is encoded in token and exists, then it's fine
        if(!$user){
            return;
        }

        return $user;
    }


    public function checkCredentials($credentials, UserInterface $user)
    {
        return true;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        return new JsonResponse([
            'message' => $exception->getMessage()
        ], 401);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return;
    }

    public function supportsRememberMe()
    {
        return false;
    }

}

并且排除了一切的security.yml只是为了检查行为。

# To get started with security, check out the documentation:
# http://symfony.com/doc/current/security.html
security:

    # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
    providers:
        in_memory:
            memory: ~

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

#################################
# Secured section
#

        # Custom authentication firewall for all request thats starts from /api
        api:
            pattern: ^/api
            guard:
                authenticators:
                    - app.token_authenticator


#################################
# Main Configuration
#

        main:
            anonymous: ~
            # activate different ways to authenticate

            # http_basic: ~
            # http://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate

            # form_login: ~
            # http://symfony.com/doc/current/cookbook/security/form_login_setup.html



    access_control:
        #- { path: ^/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        #- { path: ^/version, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        #- { path: ^/api, roles: [ROLE_USER, ROLE_API_USER] }
        - { path: ^/api, roles: IS_AUTHENTICATED_ANONYMOUSLY }

        #- { path: ^/(css|js), roles: IS_AUTHENTICATED_ANONYMOUSLY }
        #- { path: ^/(_wdt|_profiler), roles: IS_AUTHENTICATED_ANONYMOUSLY }
        #- { path: ^/, roles: ROLE_USER }

我只是将守卫放在^ / api上,并将control_access放在同一路径上以允许ANONYMOUS。我希望在具有该配置的任何路径上都不会调用防护服务,但每次都会调用它。我想我对它的运作方式缺乏了解。 我的理解是:

  • 先检查访问控制
  • 如果有匹配的行,则需要它(第一行)
  • 如果设置了IS_AUTHENTICATED_ANONYMOUSLY,则不会检查防火墙
  • 否则,下一次检查是防火墙配置,它告诉我们检查TokenAuthenticator

最初的目标是锁定 / api ,但 / api / auth / api / version 除外,无需控制即可访问。

感谢您的帮助,我想在1天半之后,我无法直接思考它。

2 个答案:

答案 0 :(得分:0)

为了记录,我设法解决了这个问题。

首先,Guard Authenticator建立在真正的用户存储库之上,这不是我们想要的。我们希望快速检查Redis,以及Mongo中的UserRepository。此外,我们不想要PHP会话,我们想要一个无状态系统(只有活动令牌是redis)。

所以我所做的是为Guard Authenticator创建一个虚拟User对象,实现所需的接口。

在访问时,我们通过使用其他数据获取redis中的令牌来检查用户是否已知。这些附加数据包括所需的User对象。

在连接时,我们实际检查数据库中的用户,如果没问题,我们创建虚拟User对象,并使用令牌以redis方式将其推送。

使用该系统一切都很好。它不是最漂亮的解决方案,但它允许在可能有多个实例的无状态环境中使用Guard。

答案 1 :(得分:0)

您好我最近遇到了同样的问题。当我们使用acces_control定制防护验证器时,缺乏关于此用例的良好文档。

首先:

  

我理解的是:

     
      
  • 先检查门禁控制
  •   

不,这句话是失败的,因此 - 它预示着对其余部分的理解。

正如您可以看到的那样here,FIREWALL(您的Token Authenticator Guard)的工作是启动身份验证过程,这是第一个事物系统会这样做 - 检查给定的凭证(如果有的话)是否会导致经过身份验证的令牌。

如果确定了 - 那么系统将执行第二次作业授权 - 例如,由于使用access_control的角色不足,可能会拒绝访问某些资源。< / p>

了解所有这些,您可以将您的守卫令牌身份验证器视为某种(我知道这不是真实的东西,只是为了清洁解释的目的)登录表单机制的抽象 - 虚拟变体,它只会当您点击登录路径时运行。只有区别在于你的后卫不会在你点击一些登录路径时运行,但是当请求有授权标题时(这完全可以自定义,因为你在getCredentials函数中这样定义了它)

Btw,在getCredentials之前调用的最新symfony there is new supports function中你应该检查请求是否有足够的标题来启动身份验证(或任何你想要的检查)。

所以基本上你说:“当请求有授权标题时运行警卫”,无论你要求哪个uri。可能是因为您以这种方式发送基本身份验证凭据 - 您的请求(即使是api /版本,尤其是api / auth)也有此标头。然后在导致你描述的行为之前触发你的警卫。

作为此类可能的解决方案之一 - 在您的警卫中,您可以将其设置为在请求具有 X-AUTH-TOKEN 标头时触发(因此您可以使用不同的标头用于公共访问路径和不同的私人)。这样你对api / version的调用就不会触发保护,甚至不会调用api / auth,因为你将使用Authorization标头而不是X-AUTH-TOKEN标头发送凭证。

如果是这样,您的警卫将不会被触发,并且那么使用已确定的未经身份验证的令牌,您会点击访问控制,这将决定您是否被允许资源(是的IS_AUTHENTICATED_ANONYMOUSLY用于该路径。)

我希望我向你解释一下。