Api Platform中Google Authenticator双因素认证:2FA码提交端点配置问题
解决Api Platform + JWT + Scheb 2FA验证码提交端点配置问题
核心问题说明
SchebTwoFactorBundle默认提供的是适配Web表单的2FA路由,不适合RESTful API场景。你需要自定义一个API端点来处理2FA验证码的提交,并结合JWT完成最终认证。
步骤1:创建2FA验证控制器
新建src/Controller/TwoFactorAuthenticationController.php,处理验证码提交请求:
<?php namespace App\Controller; use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorTokenInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; class TwoFactorAuthenticationController extends AbstractController { public function __construct( private TokenStorageInterface $tokenStorage, private AuthenticationSuccessHandlerInterface $successHandler, private AuthenticationFailureHandlerInterface $failureHandler ) {} public function verify(Request $request): JsonResponse { $token = $this->tokenStorage->getToken(); if (!$token instanceof TwoFactorTokenInterface) { return new JsonResponse(['error' => '无待处理的2FA认证请求'], 400); } $code = $request->request->get('code'); if (!$code) { return new JsonResponse(['error' => '缺少2FA验证码'], 400); } try { // 验证验证码并生成已认证令牌 $authToken = $token->createAuthenticatedToken($code); $this->tokenStorage->setToken($authToken); // 调用成功处理器生成正式JWT $response = $this->successHandler->onAuthenticationSuccess($request, $authToken); return JsonResponse::fromJsonString($response->getContent(), $response->getStatusCode()); } catch (AuthenticationException $e) { // 验证码错误返回失败响应 $response = $this->failureHandler->onAuthenticationFailure($request, $e); return JsonResponse::fromJsonString($response->getContent(), $response->getStatusCode()); } } }
步骤2:配置API端点路由
在config/routes.yaml中添加自定义2FA验证路由:
app_2fa_verify: path: /api/2fa/verify methods: [POST] controller: App\Controller\TwoFactorAuthenticationController::verify
步骤3:调整Security配置
修改config/packages/security.yaml,确保firewall支持2FA临时状态(注意不能完全设置为stateless: true,需要保留session存储临时认证信息):
security: firewalls: main: pattern: ^/api stateless: false json_login: check_path: /api/login success_handler: App\Security\LoginSuccessHandler failure_handler: App\Security\LoginFailureHandler two_factor: provider: app_user_provider success_handler: App\Security\LoginSuccessHandler failure_handler: App\Security\LoginFailureHandler logout: path: /api/logout
步骤4:自定义登录成功处理器
新建src/Security/LoginSuccessHandler.php,区分普通登录和需要2FA的场景:
<?php namespace App\Security; use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorTokenInterface; 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 Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface; class LoginSuccessHandler implements AuthenticationSuccessHandlerInterface { public function __construct(private JWTTokenManagerInterface $jwtManager) {} public function onAuthenticationSuccess(Request $request, TokenInterface $token): JsonResponse { if ($token instanceof TwoFactorTokenInterface) { // 未完成2FA,返回提示信息 return new JsonResponse([ 'login' => 'success', 'two_factor_complete' => false, 'message' => '请提交2FA验证码到 /api/2fa/verify' ]); } // 2FA验证完成,生成正式JWT $jwt = $this->jwtManager->create($token->getUser()); return new JsonResponse([ 'login' => 'success', 'two_factor_complete' => true, 'token' => $jwt ]); } }
步骤5:验证Scheb 2FA基础配置
确保config/packages/scheb_2fa.yaml中Google Authenticator已启用:
scheb_two_factor: security_tokens: - Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken - Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken google: enabled: true server_name: 你的应用名称 issuer: 你的应用名称
测试流程
- 发送POST请求到
/api/login,提交邮箱和密码,返回{"login": "success", "two_factor_complete": false} - 发送POST请求到
/api/2fa/verify,提交{"code": "你的Google验证码"},验证通过后返回带JWT的响应;验证码错误则返回错误信息
内容的提问来源于stack exchange,提问作者Frederik Hulleman
相关产品推荐
相关产品推荐

