Symfony4下静态密钥简单API认证的更优实现方案咨询
Symfony4 静态32位令牌路由鉴权实现方案
比监听内核事件更合适的实现方式有两种,均为Symfony原生支持的规范方案,无需额外引入依赖,也不会和现有安全防火墙逻辑冲突:
方案1:自定义Guard无状态认证器(适合多路由批量配置)
- 首先创建自定义认证器类,路径为
src/Security/StaticTokenAuthenticator.php,示例代码如下:
<?php namespace App\Security; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 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\Guard\AbstractGuardAuthenticator; class StaticTokenAuthenticator extends AbstractGuardAuthenticator { private string $validToken; public function __construct(string $staticAccessToken) { $this->validToken = $staticAccessToken; } public function supports(Request $request): bool { // 仅匹配你需要校验的路由,这里可以按路由名、路由前缀自定义 return str_starts_with($request->getPathInfo(), '/api/open/protected'); } public function getCredentials(Request $request): ?string { // 从请求头获取令牌,也可以自定义为Query参数、Post参数等 return $request->headers->get('X-STATIC-ACCESS-TOKEN'); } public function checkCredentials($credentials, UserInterface $user): bool { // 直接比对令牌是否一致 return $credentials === $this->validToken; } public function getUser($credentials, UserProviderInterface $userProvider): ?UserInterface { // 不需要用户实体,直接返回匿名用户即可 return new class implements UserInterface { public function getRoles(): array { return ['ROLE_PUBLIC']; } public function eraseCredentials() {} public function getUserIdentifier(): string { return 'public_user'; } }; } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse { return new JsonResponse(['message' => '无效的访问令牌'], Response::HTTP_FORBIDDEN); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response { // 认证成功继续执行控制器逻辑 return null; } public function start(Request $request, AuthenticationException $authException = null): JsonResponse { return new JsonResponse(['message' => '缺少访问令牌'], Response::HTTP_UNAUTHORIZED); } public function supportsRememberMe(): bool { return false; } }
- 配置令牌参数,在
config/services.yaml中注入预设的32位令牌:
parameters: static_access_token: '%env(STATIC_ACCESS_TOKEN)%' services: App\Security\StaticTokenAuthenticator: arguments: $staticAccessToken: '%static_access_token%'
- 在
.env文件中添加你的32位随机字符串配置:
STATIC_ACCESS_TOKEN=你的32位随机静态令牌
- 修改
config/packages/security.yaml,添加对应路由的防火墙配置,放在所有防火墙的最前面:
security: firewalls: # 静态令牌校验的路由防火墙,放在最前面 static_token_protected: pattern: ^/api/open/protected stateless: true guard: authenticators: - App\Security\StaticTokenAuthenticator # 不需要用户提供者 provider: ~ # 你原来的其他防火墙配置放在后面 # main: ...
方案2:路由条件校验(适合少量零散路由)
如果需要校验的路由数量很少,不需要批量配置,可以直接用Symfony原生的路由条件语法,不需要写额外的类:
- 注解配置示例:
/** * @Route("/api/open/protected/data", name="open_protected_data", condition="request.headers.get('X-STATIC-ACCESS-TOKEN') == parameter('static_access_token')") */ public function getProtectedData() { // 控制器逻辑 }
- YAML路由配置示例:
open_protected_data: path: /api/open/protected/data controller: App\Controller\OpenDataController::getProtectedData condition: "request.headers.get('X-STATIC-ACCESS-TOKEN') == parameter('static_access_token')"
不符合条件的请求会直接返回403响应,无需额外处理。
两种方案均只会在匹配到对应路由时才执行校验,不会全局拦截所有请求,比内核事件监听器性能更好、逻辑更内聚,和Symfony现有安全体系完全兼容,不需要额外处理异常响应逻辑。
内容的提问来源于stack exchange,提问作者Vicc
相关产品推荐
相关产品推荐

