Php 带guard的Symfony身份验证始终返回“;找不到用户名。”;

Php 带guard的Symfony身份验证始终返回“;找不到用户名。”;,php,api,symfony,access-token,guard,Php,Api,Symfony,Access Token,Guard,我知道在这个问题上有很多线索,但没有一条对我有帮助 我正在开发一个应用程序,您必须在每个请求的头中发送一个访问令牌。 我和保安一起管理这个保安 对于我的测试,当我发送假令牌或不发送时,必须根据需要调用start()或onAuthenticationFailure()方法。 但它不起作用。我每次都犯同样的错误。 看起来这些方法从未被调用过 未发送授权 GET /BileMo/web/app_dev.php/api/products/2 HTTP/1.1 Host: localhost:8888 C

我知道在这个问题上有很多线索,但没有一条对我有帮助

我正在开发一个应用程序,您必须在每个请求的头中发送一个访问令牌。 我和保安一起管理这个保安

对于我的测试,当我发送假令牌或不发送时,必须根据需要调用start()或onAuthenticationFailure()方法。 但它不起作用。我每次都犯同样的错误。 看起来这些方法从未被调用过

未发送授权

GET /BileMo/web/app_dev.php/api/products/2 HTTP/1.1
Host: localhost:8888
Content-Type: application/json

{
     "message": "Username could not be found."
}
无效的访问令牌

GET /BileMo/web/app_dev.php/api/products/2 HTTP/1.1
Host: localhost:8888
Content-Type: application/json
Authorization: *Fake Facebook Token*

{
     "message": "Username could not be found."
}
而不是:

{
       "message": "Authorization required"
}

使用正确的访问令牌,请求将正确返回给用户

请求示例:

GET /BileMo/web/app_dev.php/api/products/2 HTTP/1.1
Host: localhost:8888
Content-Type: application/json
Authorization: *Facebook Token*
以下是我的代码的重要部分:

security.yml

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_USER

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

        api_key_user_provider:
            entity:
                class: FacebookTokenBundle:User
                property: facebook_access_token

    firewalls:
        api:
            pattern: ^/api
            stateless: true
            anonymous: false
            guard:
                authenticators:
                    - AppBundle\Security\FacebookAuthenticator

        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_token_generator: security.csrf.token_manager
                login_path: /login
                check_path: /login_check

            oauth:
                resource_owners:
                    facebook:           "/login/check-facebook"
                login_path:        /login
                failure_path:      /login

                oauth_user_provider:
                    #this is my custom user provider, created from FOSUBUserProvider - will manage the
                    #automatic user registration on your site, with data from the provider (facebook. google, etc.)
                    service: my_user_provider
            logout:       true
            anonymous:    true


        login:
            pattern:  ^/login$
            security: false

            remember_me:
                secret: "%secret%"
                lifetime: 31536000 # 365 days in seconds
                path: /
                domain: ~ # Defaults to the current domain from $_SERVER

    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, role: ROLE_ADMIN }
        - { path: ^/api, role: ROLE_USER }
facebook authenticator.php


这种行为是意料之中的。AbstractGuardAuthenticator的接口过于通用,如果需要,您需要根据自己的需要对其进行定制

例如,要产生错误“需要授权”-您可以在getCredentials()方法中抛出AuthenticationException。异常将在symfony core中捕获,并最终调用方法start()

public function getCredentials(Request $request)
{
    if (!$request->headers->has('Authorization')) {
        throw new AuthenticationException();
    }
    ...
}

方法onAuthenticationFailure()通常用于在凭据错误的情况下将用户重定向到登录页面。如果标头中有API密钥,则不需要此功能。此外,在当前的实现中,当“API密钥不正确”和“未找到用户”时,如何进行分离?

上述答案在某种程度上是正确的,但有一些更正:

从验证器(guard)本身引发的任何异常都将触发AuthenticationFailure()

方法
start()
被调用,例如,当您从应用程序中抛出一个
AccessDeniedException
时,就像在控制器中一样。也许这是一个很好的使用案例,比如说,您想将一个特定路由的一个特定用户列入黑名单,并且您不想用不必要的膨胀填充您的guard authenticator

/** 
 * @Route("/something-special")
 */
public function somethingSpecial()
{
    if ($this->getUser()->getUsername() === 'a_banned_user') {
        throw new AccessDeniedException();
    }

    // ...
}
然后用以下方法进行测试:

$ curl -H "Auth-Header-Name: the_auth_token" http://site.local/something-special
{"message":"Authentication Required"}

但是,另一方面,如果由于缺少令牌头而引发异常,则将改为运行
onAuthenticationFailure()

public function getCredentials(Request $request): array
{
    if (!$request->headers->has('Authorization')) {
        throw new AuthenticationException('Authentication header missing.');
    }

    // ...
}
然后使用(注意:onAuthenticationFailure()中的AuthenticationException消息被忽略,只返回一条通用的“禁止”消息,如上所示):


它是否与头中的正确访问令牌一起工作?你只在提交错误/缺少标题时抱怨案例?您是否也可以添加一个CURL请求命令,应用程序会向该命令回复此错误?因此,我们可以看到请求URL、标题等@MaksymMoskvychev感谢您的回答,我刚刚编辑了我现在理解的onAuthenticationFailure()方法的threadOk。关于您的问题,我认为checkCredentials()方法允许我这样做,对吗?checkCredentials()是在您已经通过API键找到用户之后调用的。如果您有用户名+密码-您可以在getUser()中检查用户名,在checkCredentials()中检查密码。但在您的情况下,您只有一个字段API密钥。因此,您只能在getUser()中检查它。checkCredentials()也可用于会话中已有用户的情况。但在您的情况下,防火墙是无状态的,所以没有会话。
/** 
 * @Route("/something-special")
 */
public function somethingSpecial()
{
    if ($this->getUser()->getUsername() === 'a_banned_user') {
        throw new AccessDeniedException();
    }

    // ...
}
$ curl -H "Auth-Header-Name: the_auth_token" http://site.local/something-special
{"message":"Authentication Required"}
public function getCredentials(Request $request): array
{
    if (!$request->headers->has('Authorization')) {
        throw new AuthenticationException('Authentication header missing.');
    }

    // ...
}
$ curl http://site.local/something-special
{"message":"Forbidden"}