简体   繁体   English

Symfony守护程序:access_control无效

[英]Symfony guard : access_control has no effect

I'm new to symfony, and I'm trying to get a basic guard authenticator based on JWT. 我是symfony的新手,我正在尝试获取基于JWT的基本防护验证器。 The work is mainly from the article here, where I have removed any user check (for now): http://kolabdigital.com/lab-time/symfony-json-web-tokens-authentication-guard 这项工作主要来自此处的文章,这里我删除了所有用户检查(目前): http : //kolabdigital.com/lab-time/symfony-json-web-tokens-authentication-guard

I think there's something I don't get, because I can't make it work. 我认为我无法获得某些东西,因为我无法使其正常运行。 More precisely, it works everywhere, even in the exceptions I put in place. 更准确地说,它适用于任何地方,即使在我设置的例外情况下也是如此。

Here is the Check service, basically the same as the article, without the users management, and with a bit of logging : 这是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;
    }

}

And the security.yml, with EVERYTHING excluded, just to check the behavior. 而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 }

I just put the guard in place on ^/api, and put a control_access on the same path to allow ANONYMOUS. 我只是在^ / api上放置了防护,并在同一路径上放置了一个control_access来允许匿名。 I expect that the guard service is not called on any path with that configuration, but it's called everytime. 我希望在具有该配置的任何路径上都不会调用保护服务,但是每次都会调用它。 I guess I'm missing some understanding on how it works. 我想我对它的工作方式缺少一些了解。 What I understand is: 我的理解是:

  • Access control is checked before anything else 首先检查访问控制
  • If there's a matching line, it takes it (the first one) 如果有匹配的行,它将接受(第一个)
  • If IS_AUTHENTICATED_ANONYMOUSLY is set, then the firewall is not checked 如果设置了IS_AUTHENTICATED_ANONYMOUSLY,则不检查防火墙
  • Else, the next check is the firewall configuration, where it tells to check with the TokenAuthenticator 否则,下一个检查是防火墙配置,它告诉您使用TokenAuthenticator进行检查

The initial aim is to lock /api , except /api/auth and /api/version that can be accessed without control. 最初的目的是锁定/ api ,但/ api / auth/ api / version除外,后者无需控制即可访问。

Thanks for the help, I think after 1 day and a half on it, I'm not able to think straight about it. 感谢您的帮助,我认为经过1天半的时间,我无法对此进行思考。

For the record, I managed to workaround this issue. 作为记录,我设法解决此问题。

First, Guard Authenticator is built upon real User Repository, which is not what we want. 首先,Guard Authenticator建立在真实的用户存储库上,这不是我们想要的。 We want a fast check with Redis, and a UserRepository in Mongo. 我们希望使用Redis进行快速检查,并在Mongo中使用UserRepository。 Also, we don't want PHP sessions, we want a stateless system (only the active token is in redis). 此外,我们不需要PHP会话,我们需要无状态系统(仅活动令牌位于redis中)。

So what I did is create a dummy User object for Guard Authenticator, implementing the needed interface. 因此,我要做的是为Guard Authenticator创建一个虚拟User对象,实现所需的接口。

On access, we check if the user is already known by getting its token in redis, with additional data. 在访问时,我们通过在Redis中获取其令牌以及其他数据来检查用户是否已经知道。 These additional data include the needed User object. 这些附加数据包括所需的User对象。

On connection, we actually check the user in database, and if it's fine, we create the dummy User object, and push it in redis with the token. 在连接上,我们实际上检查数据库中的用户,如果还可以,我们创建虚拟User对象,并使用令牌将其压入redis。

With that system everything is fine. 使用该系统,一切都很好。 It's not the prettiest solution, but it allows using Guard in a stateless environment with possibly multiple instances. 这不是最漂亮的解决方案,但是它允许在可能有多个实例的无状态环境中使用Guard。

Hello i recently got the same problem. 您好,我最近遇到了同样的问题。 There is a lack of well documentation about this use-case when we have custom guard authenticator with acces_control. 当我们具有带有acces_control的自定义防护身份验证器时,缺少关于此用例的完善文档。

First of all: 首先:

What I understand is: 我的理解是:

  • Access control is checked before anything else 首先检查访问控制

No, This sentence is FAIL and because of this - it projects the understanding of the rest. 不,这句话是失败的,因此,它使您对其余的内容有所了解。

As you can see for example here , the job of FIREWALL (your Token Authenticator Guard) is to start the authentication process which is the first thing the system does - to check if the given credentials (if any) will result in authenticated token or not. 如您在此处看到的那样,FIREWALL(您的令牌身份验证器保护器)的工作是启动身份验证过程,这是系统要做的第一件事-检查给定的凭据(如果有)是否会生成经过身份验证的令牌。

If this is determined - then system will do his second job which is authorization - for example to possibly deny access to certain resources because of insufficient role using access_control. 如果确定,则系统将执行其第二项工作即授权 -例如,由于使用access_control的角色不足,可能拒绝对某些资源的访问。

Knowing all of this, you can think of your guard token authenticator as some kind of (i know it is not the real thing but just for purpose of clean explanation) abstract-virtual-variation of login form mechanism, which will only run when you hit your login path. 知道了所有这些之后,您就可以将您的保护令牌身份验证器视为某种形式(我知道这不是真实的东西,而只是为了进行清晰的解释)登录表单机制的抽象虚拟变量,仅在您登录时运行点击您的登录路径。 Only difference is here your guard will run not when you hit some login path, but when the request have Authorization header (and this is fully customizable as you defined it like this in getCredentials function) 唯一的区别是,当您遇到某个登录路径时,防护将不会运行,而是在请求具有Authorization标头时运行(并且可以完全自定义,就像您在getCredentials函数中定义的那样)

Btw, in newest symfony there is new supports function which is called before getCredentials in which you should check does request have sufficient headers to start Authentication (or whatever check you want). 顺便说一句,在最新的symfony中, 有一个新的支持功能 ,该功能在getCredentials之前调用,您应该在其中检查请求是否具有足够的标头以启动Authentication(或所需的任何检查)。

So basically you said: "run guard when request have Authorization header" , no matter which uri you requests. 因此,基本上您说: “当请求具有授权标头时,请谨慎对待” ,无论您请求哪个uri。 Probably because you send your basic auth credentials that way - your request (even for api/version and especially for api/auth) have this header. 可能是因为您以这种方式发送了基本身份验证凭据-您的请求(即使是api / version,尤其是api / auth)也具有此标头。 Then your guard is triggered before anything else which result in behavior you described. 然后,会先触发您的警卫,再导致您描述的行为。

As a one of possible solutions for this - inside your guard, you could set it to trigger when request have X-AUTH-TOKEN header (so you can use different headers for public access paths and different for private). 作为一种可能的解决方案-在警卫人员内部,您可以将其设置为在请求具有X-AUTH-TOKEN标头时触发(因此,可以将不同的标头用于公共访问路径,而将不同的标头用于私有)。 That way your call to api/version wont trigger guard, and even call for api/auth wont do it as you will be sending credentials using Authorization header, not X-AUTH-TOKEN header. 这样,您对api / version的调用将不会触发防护,甚至对api / auth的调用也不会触发防护,因为您将使用Authorization标头而不是X-AUTH-TOKEN标头发送凭据。

If so your guard will not be triggered and THEN with determined unauthenticated token you hit your accesss control which will decide if you are allowed to resource or not (yes for IS_AUTHENTICATED_ANONYMOUSLY for that path). 如果是这样,您的防护将不会被触发, 然后使用已确定的未经身份验证的令牌来触发THEN ,您将访问访问控件 ,该控件将决定是否允许您进行资源分配(该路径的IS_AUTHENTICATED_ANONYMOUSLY是)。

I hope i explained to you a bit. 我希望我向您解释了一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM