使用 Symfony 5.3 检查 JWT (Firebase) 令牌

Posted

技术标签:

【中文标题】使用 Symfony 5.3 检查 JWT (Firebase) 令牌【英文标题】:Check JWT (Firebase) Token with Symfony 5.3 【发布时间】:2021-12-08 23:44:51 【问题描述】:

我正在开发一个 API,并且我已经实现了一个 JWT 以使其无状态。我创建了一个 AuthController,它在登录信息正确时返回一个 JWT。在这里你可以看到生成令牌的返回码:

/* RETURN MESSAGE */
$body = [
    'auth_token' => $jwt,
];
$json = new JsonResponse($body);
$json->setStatusCode(201, "Created");   // Headers
return $json;

这是我运行身份验证方法时的结果,而不是 URL localhost:8000/authenticate。 现在,我需要做的是,当用户尝试获取另一个 / URL 时,如果他没有在请求的标头中传递 Bearer 令牌,则程序不允许他访问它。但它不起作用。该平台始终允许我输入任何 URL,而无需在标题中设置授权。 这是我的安全文件,我尝试在其中进行设置:

security:
    # https://symfony.com/doc/current/security/authenticator_manager.html
    enable_authenticator_manager: true

    # https://symfony.com/doc/current/security.html#c-hashing-passwords
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

    encoders:
        App\Entity\ATblUsers:
            algorithm: bcrypt

    providers:
        users_in_memory:  memory: null 

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

        main:
            # Anonymous property is no longer supported by Symfony. It is commented by now, but it will be deleted in
            # future revision:
            # anonymous: true
            guard:
                authenticators:
                    - App\Security\JwtAuthenticator
            lazy: true
            provider: users_in_memory

            # activate different ways to authenticate
            # https://symfony.com/doc/current/security.html#firewalls-authentication

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true
            stateless: true

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        # -  path: ^/admin, roles: ROLE_ADMIN 
        # -  path: ^/profile, roles: ROLE_USER 

最后,这是我的App\Security\JwtAuthenticator

namespace App\Security;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
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\Core\Authentication\Token\TokenInterface;
use Firebase\JWT\JWT;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;

class JwtAuthenticator extends AbstractGuardAuthenticator

    private $em;
    private $params;

    public function __construct(EntityManagerInterface $em, ContainerBagInterface $params)
    
        $this->em = $em;
        $this->params = $params;
    

    public function start(Request $request, AuthenticationException $authException = null): JsonResponse
    
        $body = [
            'message' => 'Authentication Required',
        ];
        return new JsonResponse($body, Response::HTTP_UNAUTHORIZED);
    

    public function supports(Request $request): bool
    
        return $request->headers->has('Authorization');
    

    public function getCredentials(Request $request)
    
        return $request->headers->get('Authorization');
    

    public function getUser($credentials, UserProviderInterface $userProvider)
    
        try
            $credentials = str_replace('Bearer ', '', $credentials);
            $jwt = (array) JWT::decode($credentials, $this->params->get('jwt_secret'), ['HS256']);
            return $this->em->getRepository('App:ATblUsers')->find($jwt['sub']);
        catch (\Exception $exception)
            throw new AuthenticationException($exception->getMessage());
        

    

    public function checkCredentials($credentials, UserInterface $user)
    

    

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse
    
        return new JsonResponse([
            'message' => $exception->getMessage()
        ], Response::HTTP_UNAUTHORIZED);
    

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

    public function supportsRememberMe(): bool
    
        return false;
    

我查看了很多网站和教程,但没有人完全按照我的需要进行操作,或者正在实施与我的需要不匹配的非常基本的功能。几乎所有网站都使用 Symfony 4 来解释这一点,但我使用的是 Symfony 5,因此教程中使用的许多功能都已弃用。有人知道我错过了什么吗?

【问题讨论】:

【参考方案1】:

您可能在security.yaml 中缺少access_control 配置:

security:
    # https://symfony.com/doc/current/security/authenticator_manager.html
    enable_authenticator_manager: true

    # https://symfony.com/doc/current/security.html#c-hashing-passwords
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

    encoders:
        App\Entity\ATblUsers:
            algorithm: bcrypt

    providers:
        users_in_memory:  memory: null 

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

        main:
            # Anonymous property is no longer supported by Symfony. It is commented by now, but it will be deleted in
            # future revision:
            # anonymous: true
            guard:
                authenticators:
                    - App\Security\JwtAuthenticator
            lazy: true
            provider: users_in_memory

            # activate different ways to authenticate
            # https://symfony.com/doc/current/security.html#firewalls-authentication

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true
            stateless: true

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        -  path: ^/authenticate, roles: PUBLIC_ACCESS 
        -  path: ^/, roles: IS_AUTHENTICATED_FULLY 

【讨论】:

你可能是对的,我也试过了。但是当添加access_control时,我什至无法访问/authenticate,所以我永远无法登录生成JWT令牌。 哦,你是对的,你需要用PUBLIC_ACCESS添加另一个路径,所以它看起来像这样:access_control: - path: ^/authenticate, roles: PUBLIC_ACCESS - path: ^/ ,角色:IS_AUTHENTICATED_FULLY 对于和我一样情况的每个人来说,这个答案将解决问题。但也可以检查@Abdelhak ouaddi 的答案。因此,您可以尝试使用简单的捆绑包而不是 firebase。我终于用 Slimu 所说的话来工作了。有必要对我的原始代码进行一些修改,但最终可以正常工作。【参考方案2】:

我没有详细查看您的代码,我只是想告诉您,您正在努力做的事情已经存在于一个维护和记录良好的包中,您不需要硬编码,我真的邀请您使用它很有用。

https://github.com/lexik/LexikJWTAuthenticationBundle

【讨论】:

我开始使用 LexikJWTAuthenticationBundle,但不适用于我的代码。我遇到了很多错误,所以最终决定使用 Firebase JWT 更改它并手动进行,这样我就可以将其调整为我的代码并避免所有这些错误。我知道这不是最直观的,但如果它有效,我会很高兴。 是的,我明白了,有时会发生,祝你好运

以上是关于使用 Symfony 5.3 检查 JWT (Firebase) 令牌的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Encore 在 Symfony 5.3 上正确安装和使用引导程序

配置 Symfony 5.3 以使用专用的 MongoDB 数据库进行测试

symfony 5.3 连接微软 sql server mssql

Symfony - Composer 更新 - 需要使用旧的依赖项(PHP 5.4 到 PHP 5.3)

“名称解析中的临时失败”:Symfony 5.3 使用 Doctrine 和 Docker 连接到 MariaDB

Symfony 4 Restful API 使用 JWT 和 facebook 登录