[英]Check JWT (Firebase) Token with Symfony 5.3
I'm working on an API, and I had implemented a JWT to make it stateless.我正在开发一个 API,并且我已经实现了一个 JWT 以使其无状态。 I created an AuthController, which returns a JWT when login information is correct.
我创建了一个 AuthController,它在登录信息正确时返回一个 JWT。 Here you can see the return code that generates the token:
在这里您可以看到生成令牌的返回码:
/* RETURN MESSAGE */
$body = [
'auth_token' => $jwt,
];
$json = new JsonResponse($body);
$json->setStatusCode(201, "Created"); // Headers
return $json;
This is the result when I run the authenticate method, un the URL localhost:8000/authenticate
.这是我在 URL
localhost:8000/authenticate
运行身份验证方法时的结果。
Now, what I would need to do is that, when a user tries to get another /
URL, the program doesn't allow him to reach it if he's not passing the Bearer token in the request's header.现在,我需要做的是,当用户尝试获取另一个
/
URL 时,如果他没有在请求的标头中传递 Bearer 令牌,则程序不允许他访问它。 But it's not working.但它不起作用。 The platform always allows me to enter any URL without setting an Authorization in the header.
该平台始终允许我输入任何 URL,而无需在标题中设置授权。
Here's my security file, where I tried to set this:这是我的安全文件,我尝试在其中进行设置:
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 }
And, finally, here's my App\Security\JwtAuthenticator
:最后,这是我的
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;
}
}
I've been looking at a lot of websites and tutorials, but not anyone is doing exactly what I need or are implementing very basic functionalities that don't match with what I need.我一直在看很多网站和教程,但没有人在做我需要的,或者正在实现与我需要的不匹配的非常基本的功能。 Almost all of that websites explain this using Symfony 4, but I'm using Symfony 5, so a lot of functions that use in tutorials are deprecated.
几乎所有这些网站都使用 Symfony 4 来解释这一点,但我使用的是 Symfony 5,因此教程中使用的许多功能都已弃用。 Does someone know what I am missing?
有人知道我错过了什么吗?
You are probably missing access_control
configuration in security.yaml
:您可能在
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 }
I have not looked at your code in detail, I just want to tell you that what you are doing hard already exists in a bundle maintained and well documented and you will not need to hardcode, I really invite you to use it is very useful.我没有详细查看您的代码,我只想告诉您,您正在做的事情已经存在于一个维护和记录良好的包中,您不需要硬编码,我真的邀请您使用它非常有用。
https://github.com/lexik/LexikJWTAuthenticationBundle https://github.com/lexik/LexikJWTAuthenticationBundle
Solution: Symfony 6解决方案:Symfony 6
In my case, I came here looking for a Symfony 6 solution.就我而言,我来这里是为了寻找 Symfony 6 的解决方案。
I had to install Firebase PHP SDK (composer require kreait/firebase-php)我必须安装 Firebase PHP SDK(作曲家需要 kreait/firebase-php)
I had to download authentication json file from firebase (Project configuration -> Service accounts) in order to init Firebase SDK我必须从firebase(项目配置->服务帐户)下载身份验证json文件才能初始化Firebase SDK
App\Security\JWTAuthenticator:应用\安全\JWTAuthenticator:
<?php
namespace App\Security;
use App\Entity\User;
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\Authentication\Token\TokenInterface;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Cache\Simple\FilesystemCache;
use Kreait\Firebase;
use Firebase\Auth\Token\Exception\InvalidToken;
class JWTAuthenticator extends AbstractAuthenticator
{
private $em;
private $params;
private $projectDirectory;
private $firebase;
public function __construct(string $projectDirectory, EntityManagerInterface $em, ContainerBagInterface $params)
{
$this->projectDirectory = $projectDirectory;
$this->em = $em;
$this->params = $params;
$this->firebase = (new Firebase\Factory())->withServiceAccount($this->projectDirectory.'/firebase-authentication.json');
}
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 onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse
{
return new JsonResponse([
'message' => $exception->getMessage()
], Response::HTTP_UNAUTHORIZED);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response
{
return null;
}
public function supportsRememberMe(): bool
{
return false;
}
public function authenticate(Request $request): Passport
{
try{
$credentials = str_replace('Bearer ', '', $this->getCredentials($request));
$firebaseAuth = $this->firebase->createAuth();
try {
$verifiedIdToken = $firebaseAuth->verifyIdToken($credentials);
$tokenClaims = $verifiedIdToken->claims();
$sub = $tokenClaims->get('sub');
$email = $tokenClaims->get('email');
if ($verifiedIdToken->isExpired(new \DateTime())) {
throw new AuthenticationException("Token expired.");
}
} catch (Firebase\Exception\AuthException | Firebase\Exception\FirebaseException $e) {
throw new AuthenticationException($e->getMessage());
}
}
catch (\Exception $exception){
throw new AuthenticationException($exception->getMessage());
}
return new SelfValidatingPassport(new UserBadge($email));
}
}
My security.yaml我的安全.yaml
security:
enable_authenticator_manager: true
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
stateless: true
provider: app_user_provider
custom_authenticator: App\Security\JWTAuthenticator
# 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: ^/api/docs, roles: PUBLIC_ACCESS }
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.