【问题标题】:Symfony guard : access_control has no effectSymfony 守卫:access_control 无效
【发布时间】:2017-07-23 10:22:13
【问题描述】:

我是 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 以允许匿名。我希望在具有该配置的任何路径上都不会调用警卫服务,但每次都会调用它。我想我对它的工作原理有一些了解。 我的理解是:

  • 首先检查访问控制
  • 如果有匹配的行,就取它(第一个)
  • 如果设置了 IS_AUTHENTICATED_ANONYMOUSLY,则不检查防火墙
  • 否则,下一个检查是防火墙配置,它告诉使用 TokenAuthenticator 进行检查

最初的目的是锁定 /api,除了 /api/auth/api/version 可以不受控制地访问。

感谢您的帮助,我想在 1 天半之后,我无法想清楚。

【问题讨论】:

    标签: php symfony


    【解决方案1】:

    您好,我最近遇到了同样的问题。当我们使用带有 acces_control 的自定义保护身份验证器时,缺少有关此用例的良好文档。

    首先:

    我的理解是:

    • 首先检查访问控制

    不,这句话是失败的,因此 - 它投射了对其余部分的理解。

    如您所见,例如 here ,FIREWALL(您的 Token Authenticator Guard)的工作是启动 authentication 过程,这是系统的第一确实 - 检查给定的凭据(如果有)是否会产生经过身份验证的令牌。

    如果确定了这一点 - 那么系统将执行他的第二项工作,即授权 - 例如,由于使用 access_control 的角色不足,可能会拒绝访问某些资源。

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

    顺便说一句,在最新的 symfony there is new supports function 中,它在 getCredentials 之前调用,您应该在其中检查请求是否有足够的标头来启动身份验证(或任何您想要的检查)。

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

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

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

    希望我向你解释一下。

    【讨论】:

    • 感谢您的解释,但问题并不是真正的身份验证或其他问题。只是这个用例从一开始就没有考虑过。像我一样实施存储库让我们最终可以做我们想做的事,我想这是在这种情况下我们能做的最好的事情。我希望 Symfony 会重新考虑它的保护系统以适应新的范例,但就我而言,我不再使用它了 :)
    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-08
      • 2017-11-27
      • 2013-03-31
      • 2011-12-22
      相关资源
      最近更新 更多