指尖上的记忆指尖上的记忆
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub
  • symfony项目列表

    • 第一课:简介
    • 第二课:舞台搭建
    • 第三课:登陆模块

      • 模块介绍
      • 详细配置
      • 相关表迁移
      • 测试登陆功能
      • 自定义handler

自定义handler, 主要是自定义json_login的success_handler 和 failure_handler

1.修改security.yaml配置
    providers:
#        users_in_memory: { memory: null }
        app_user_provider:
            entity:
                class: App\Entity\User
    firewalls:
        login:
            pattern: ^/api/(login|token/refresh)
            stateless: true
            json_login:
                check_path: /api/login_check
                success_handler: App\Security\CustomAuthenticationSuccessHandler
                failure_handler: App\Security\CustomAuthenticationFailureHandler
            refresh_jwt:
                check_path: /api/token/refresh
                provider: app_user_provider
        api:
            pattern: ^/api
            stateless: true
            jwt:
                provider: app_user_provider
                authenticator: app.custom_jwt_authenticator
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

    # 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/login_check, roles: PUBLIC_ACCESS }
        - { path: ^/api/(login|token/refresh), roles: PUBLIC_ACCESS }
        - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }
        
主要修改内容为:
修改了默认的 success_handler 和 failure_handler
对于 api 防火墙,添加了自定义的jwt认证
2.定义 CustomAuthenticationSuccessHandler 和 CustomAuthenticationFailureHandler
//CustomAuthenticationSuccessHandler
<?php

namespace App\Security;

use Gesdinet\JWTRefreshTokenBundle\Model\RefreshTokenManagerInterface;
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
{
    public function __construct(
        private JWTTokenManagerInterface $jwtManager,
        private RefreshTokenManagerInterface $refreshTokenManager,
        private ParameterBagInterface $params,
    ) {}

    public function onAuthenticationSuccess(Request $request, TokenInterface $token): JsonResponse
    {
        $user = $token->getUser();
        $jwt = $this->jwtManager->create($user);

        // 读取配置的 ttl
        $ttl = $this->params->get('gesdinet_jwt_refresh_token.ttl');
        $validUntil = (new \DateTimeImmutable())->modify("+$ttl seconds");

        $refreshTokenString = $this->generateSecureRefreshToken();

        $refreshToken = new RefreshToken();
        $refreshToken
            ->setRefreshToken($refreshTokenString)
            ->setUsername($user->getUserIdentifier())
            ->setValid($validUntil);

        $this->refreshTokenManager->save($refreshToken);

        return new JsonResponse([
            'access_token' => $jwt,
            'refresh_token' => $refreshTokenString,
        ]);
    }

    private function generateSecureRefreshToken(): string
    {
        try {
            return 'rt_' . bin2hex(random_bytes(40)); // 40 bytes = 80 hex chars
        } catch (\Exception $e) {
            throw new \RuntimeException('can not generate a security refresh token', 0, $e);
        }
    }
}

//CustomAuthenticationFailureHandler
<?php

namespace App\Security;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;

class CustomAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface
{
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse
    {
        $message = 'Authentication failed.';

        if ($exception instanceof BadCredentialsException) {
            $message = 'Invalid username or password.';
        } elseif ($exception instanceof CustomUserMessageAuthenticationException) {
            $message = $exception->getMessage();
        }

        return new JsonResponse(['status' => 401, 'message' => $message], JsonResponse::HTTP_UNAUTHORIZED);
    }
}
3.自定义 authenticator: app.custom_jwt_authenticator
//CustomJwtAuthenticator
<?php

namespace App\Security;

use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\JWTAuthenticator;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException as SymfonyAuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class CustomJwtAuthenticator extends JWTAuthenticator
{
    public function __construct(
        JWTTokenManagerInterface $jwtManager,
        EventDispatcherInterface $eventDispatcher,
        TokenExtractorInterface $tokenExtractor,
        UserProviderInterface $userProvider,
        ?TranslatorInterface $translator = null,
    ) {
        parent::__construct($jwtManager, $eventDispatcher, $tokenExtractor, $userProvider, $translator);
    }

    public function supports(Request $request): ?bool
    {
        return str_starts_with($request->getPathInfo(), '/api/');
    }

    public function authenticate(Request $request): Passport
    {
        try {
            $passport = parent::authenticate($request);
        } catch (\LogicException $exception) {
            throw new CustomUserMessageAuthenticationException("User not logged in. Please authenticate to proceed.:".$exception->getMessage());
        } catch (SymfonyAuthenticationException $exception) {
            throw new CustomUserMessageAuthenticationException("Login failed.:".$exception->getMessage());
        }

        return $passport;
    }
}

然后在services.yaml下配置
    app.custom_jwt_authenticator:
        class: App\Security\CustomJwtAuthenticator
        parent: lexik_jwt_authentication.security.jwt_authenticator
4.再次登陆测试,依然可以登陆成功
5.使用access_token访问指定的路由
请求地址:
http://symfony.api.local/api/lucky/number

参数:
header 参数
Authorization:Bearer {{access_token}}

响应:
{
	"code": 401,
	"message": "Login failed.:"
}
发现是使用的是错误的token

使用最新的access_token:
<html>
   <body>
      Lucky number: 44
   </body>
</html>

请求成功
Prev
测试登陆功能