本文目录导读:

- 基础认证表单登录 (Form Login)
- API Token 认证
- JWT 认证 (LexikJWTAuthenticationBundle)
- 双因素认证 (Two-Factor Authentication)
- OAuth / Social Login 认证
- LDAP 认证
- 自定义认证 Provider
- 组合认证 (Multiple Authenticators)
- 配置示例
- 自定义访问控制
我来详细介绍一下 Symfony Authenticator 的多种实现方式。
基础认证表单登录 (Form Login)
// src/Security/LoginFormAuthenticator.php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
public function __construct(
private UrlGeneratorInterface $urlGenerator
) {
}
public function authenticate(Request $request): Passport
{
$email = $request->request->get('email', '');
$request->getSession()->set(Security::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
new RememberMeBadge(),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate('app_home'));
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}
API Token 认证
// src/Security/ApiTokenAuthenticator.php
namespace App\Security;
use App\Repository\ApiTokenRepository;
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\Exception\CustomUserMessageAuthenticationException;
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;
class ApiTokenAuthenticator extends AbstractAuthenticator
{
public function __construct(
private ApiTokenRepository $apiTokenRepository
) {
}
public function supports(Request $request): ?bool
{
return $request->headers->has('X-AUTH-TOKEN');
}
public function authenticate(Request $request): Passport
{
$apiToken = $request->headers->get('X-AUTH-TOKEN');
if (null === $apiToken) {
throw new CustomUserMessageAuthenticationException('No API token provided');
}
$apiToken = $this->apiTokenRepository->findOneBy(['token' => $apiToken]);
if (null === $apiToken || $apiToken->isExpired()) {
throw new CustomUserMessageAuthenticationException('Invalid or expired API token');
}
return new SelfValidatingPassport(
new UserBadge($apiToken->getUser()->getUserIdentifier())
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null; // 继续处理请求
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$data = [
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
];
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
}
JWT 认证 (LexikJWTAuthenticationBundle)
// src/Security/JWTAuthenticator.php
namespace App\Security;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\JWTAuthenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
class JWTAuthenticator extends AbstractAuthenticator
{
public function supports(Request $request): ?bool
{
return $request->headers->has('Authorization')
&& str_starts_with($request->headers->get('Authorization'), 'Bearer ');
}
public function authenticate(Request $request): Passport
{
$token = substr($request->headers->get('Authorization'), 7);
// 解析和验证 JWT token
$jwtToken = $this->jwtManager->decode($token);
if (null === $jwtToken) {
throw new CustomUserMessageAuthenticationException('Invalid JWT token');
}
return new SelfValidatingPassport(
new UserBadge($jwtToken['username'], function ($userIdentifier) {
return $this->userProvider->loadUserByIdentifier($userIdentifier);
})
);
}
}
双因素认证 (Two-Factor Authentication)
// src/Security/TwoFactorAuthenticator.php
namespace App\Security;
use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
class TwoFactorAuthenticator extends AbstractAuthenticator
{
public function authenticate(Request $request): Passport
{
$username = $request->request->get('username');
$password = $request->request->get('password');
$twoFactorCode = $request->request->get('2fa_code');
return new Passport(
new UserBadge($username),
new PasswordCredentials($password),
[
new TwoFactorCodeBadge($twoFactorCode),
new RememberMeBadge(),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($token instanceof TwoFactorToken) {
// 需要完成双因素认证
return new RedirectResponse('/2fa');
}
return new RedirectResponse('/dashboard');
}
}
OAuth / Social Login 认证
// src/Security/OAuthAuthenticator.php
namespace App\Security;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
class GoogleAuthenticator extends AbstractAuthenticator
{
public function supports(Request $request): ?bool
{
return $request->attributes->get('_route') === 'connect_google_check';
}
public function authenticate(Request $request): Passport
{
$client = $this->clientRegistry->getClient('google');
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function () use ($accessToken, $client) {
$googleUser = $client->fetchUserFromToken($accessToken);
// 查找或创建用户
$existingUser = $this->userRepository->findOneBy([
'googleId' => $googleUser->getId()
]);
if ($existingUser) {
return $existingUser;
}
// 创建新用户
$user = new User();
$user->setEmail($googleUser->getEmail());
$user->setGoogleId($googleUser->getId());
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
})
);
}
}
LDAP 认证
// src/Security/LdapAuthenticator.php
namespace App\Security;
use Symfony\Component\Ldap\Ldap;
use Symfony\Component\Ldap\Security\LdapBadge;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
class LdapAuthenticator extends AbstractLoginFormAuthenticator
{
public function __construct(
private Ldap $ldap,
private string $ldapDn = 'ou=users,dc=example,dc=com',
private string $ldapSearchDn = 'cn=admin,dc=example,dc=com',
private string $ldapSearchPassword = 'password'
) {
}
public function authenticate(Request $request): Passport
{
$username = $request->request->get('username', '');
$password = $request->request->get('password', '');
return new Passport(
new UserBadge($username, function ($userIdentifier) {
// 从 LDAP 获取用户
$user = $this->userProvider->loadUserByIdentifier($userIdentifier);
return $user;
}),
new PasswordCredentials($password),
[
new LdapBadge(
$this->ldap,
$this->ldapDn,
$this->ldapSearchDn,
$this->ldapSearchPassword
),
]
);
}
}
自定义认证 Provider
// src/Security/CustomUserProvider.php
namespace App\Security;
use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class CustomUserProvider implements UserProviderInterface, PasswordUpgraderInterface
{
public function __construct(
private UserRepository $userRepository
) {
}
public function loadUserByIdentifier(string $identifier): UserInterface
{
// 支持通过邮箱或用户名查找
$user = $this->userRepository->findOneBy(['email' => $identifier])
?? $this->userRepository->findOneBy(['username' => $identifier]);
if (!$user) {
throw new UserNotFoundException(sprintf('User "%s" not found.', $identifier));
}
return $user;
}
public function refreshUser(UserInterface $user): UserInterface
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
}
return $this->loadUserByIdentifier($user->getUserIdentifier());
}
public function supportsClass(string $class): bool
{
return User::class === $class || is_subclass_of($class, User::class);
}
public function upgradePassword(UserInterface $user, string $newHashedPassword): void
{
$user->setPassword($newHashedPassword);
$this->entityManager->persist($user);
$this->entityManager->flush();
}
}
组合认证 (Multiple Authenticators)
# config/packages/security.yaml
security:
firewalls:
main:
pattern: ^/
stateless: false
# 组合多个认证器
entry_point: App\Security\LoginFormAuthenticator
custom_authenticators:
- App\Security\LoginFormAuthenticator
- App\Security\ApiTokenAuthenticator
- App\Security\JWTAuthenticator
# 设置认证器的优先级
# 第一个匹配的会被使用
json_login:
check_path: /api/login
form_login:
login_path: /login
check_path: /login_check
logout:
path: /logout
target: /
配置示例
# config/packages/security.yaml
security:
enable_authenticator_manager: true
password_hashers:
App\Entity\User: '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
provider: app_user_provider
# 自定义认证器
custom_authenticators:
- App\Security\LoginFormAuthenticator
- App\Security\ApiTokenAuthenticator
# 登录配置
form_login:
login_path: app_login
check_path: app_login
enable_csrf: true
# 退出登录
logout:
path: app_logout
# 退出后跳转
target: app_login
# 记住我功能
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1周
path: /
always_remember_me: true
# 访问控制
access_denied_handler: App\Security\AccessDeniedHandler
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
- { path: ^/login, roles: PUBLIC_ACCESS }
- { path: ^/api/public, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: ROLE_API_USER }
自定义访问控制
// src/Security/Voter/PostVoter.php
namespace App\Security\Voter;
use App\Entity\Post;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
class PostVoter extends Voter
{
const VIEW = 'view';
const EDIT = 'edit';
const DELETE = 'delete';
protected function supports(string $attribute, $subject): bool
{
if (!in_array($attribute, [self::VIEW, self::EDIT, self::DELETE])) {
return false;
}
if (!$subject instanceof Post) {
return false;
}
return true;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
/** @var Post $post */
$post = $subject;
switch ($attribute) {
case self::VIEW:
return $this->canView($post, $user);
case self::EDIT:
return $this->canEdit($post, $user);
case self::DELETE:
return $this->canDelete($post, $user);
}
return false;
}
private function canView(Post $post, UserInterface $user): bool
{
// 所有人都可以查看公开文章
if ($post->isPublished()) {
return true;
}
// 作者可以查看未发布的文章
return $user === $post->getAuthor();
}
private function canEdit(Post $post, UserInterface $user): bool
{
return $user === $post->getAuthor() || in_array('ROLE_ADMIN', $user->getRoles());
}
private function canDelete(Post $post, UserInterface $user): bool
{
return $user === $post->getAuthor();
}
}
Symfony 提供了多种灵活的认证方式:
- 表单登录 - 基本的用户名/密码认证
- API Token - 适用于 REST API
- JWT - 无状态认证,适合微服务
- 双因素认证 - 增强安全性
- OAuth/社交登录 - 第三方登录
- LDAP - 企业级集成
- 自定义 Provider - 灵活的认证逻辑
- 组合认证 - 支持多认证方式并存
选择哪种认证方式取决于你的应用需求:
- Web 应用:表单登录 + 记住我
- API:JWT 或 API Token
- 企业应用:LDAP + 双因素认证
- 社交平台:OAuth
需要更具体的实现细节吗?
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播