You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 6中如何通过EveOnline SSO实现本地客户端认证

解决Symfony 6中EveOnline SSO登录后本地认证状态未设置的问题

核心结论

是的,你需要构建自定义认证器。Symfony 6的安全组件依赖认证器管理用户认证状态,你用的是第三方OAuth2登录(非表单密码模式),默认认证器完全不适用,必须自定义适配EveOnline SSO的认证逻辑。


分步实现方案

1. 创建自定义认证器类

新建src/Security/EveOnlineAuthenticator.php,继承Symfony提供的AbstractAuthenticator(减少重复代码),实现核心认证逻辑:

namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
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\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
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 EveOnlineAuthenticator extends AbstractAuthenticator
{
    public function __construct(
        private EntityManagerInterface $em,
        private UrlGeneratorInterface $urlGenerator
    ) {}

    // 判断当前请求是否需要该认证器处理
    public function supports(Request $request): ?bool
    {
        // 检查会话是否有Eve的token,且请求路径是需要认证的会员区
        return $request->getSession()->has('token') && str_starts_with($request->getPathInfo(), '/member');
    }

    // 执行认证逻辑
    public function authenticate(Request $request): Passport
    {
        $token = $request->getSession()->get('token');
        // 从Eve返回的token中获取角色ID(需和你User实体存储的字段对应)
        $characterId = $token->getResourceOwner()->getId();

        return new SelfValidatingPassport(
            new UserBadge($characterId, function ($userIdentifier) {
                // 根据角色ID查找本地用户
                $user = $this->em->getRepository(User::class)->findOneBy(['eveCharacterId' => $userIdentifier]);
                if (!$user) {
                    throw new UserNotFoundException();
                }
                return $user;
            })
        );
    }

    // 认证成功后的处理(直接放行即可)
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        return null;
    }

    // 认证失败后的处理(跳回Eve登录页)
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        $request->getSession()->remove('token');
        return new RedirectResponse($this->urlGenerator->generate('eve_login'));
    }
}

2. 配置Security.yaml

修改config/packages/security.yaml,把自定义认证器绑定到防火墙,并配置用户提供者:

security:
    enable_authenticator_manager: true
    password_hashers:
        App\Entity\User:
            algorithm: auto

    # 配置用户提供者:根据Eve角色ID查找本地用户
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: eveCharacterId

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider
            # 绑定自定义认证器
            custom_authenticators:
                - App\Security\EveOnlineAuthenticator
            logout:
                path: app_logout
                invalidate_session: true

    access_control:
        # 会员区需要ROLE_USER权限(需在User实体的getRoles()中返回)
        - { path: ^/member, roles: ROLE_USER }

3. 回调控制器补充会话设置

在你的SecurityController回调方法中,获取Eve的token并完成用户存储/更新后,把token存入会话,然后跳转到会员区:

// 回调方法示例
public function callback(Request $request, ClientRegistry $clientRegistry)
{
    $client = $clientRegistry->getClient('eveonline');
    $token = $client->getAccessToken('authorization_code', [
        'code' => $request->query->get('code')
    ]);

    // 保存/更新本地User实体逻辑...

    // 将Eve的token存入会话,供认证器读取
    $request->getSession()->set('token', $token);

    // 跳转到会员区,此时认证器会自动触发认证
    return $this->redirectToRoute('member_area');
}

4. 确保User实体符合要求

你的User实体必须实现Symfony\Component\Security\Core\User\UserInterface,关键方法示例:

public function getUserIdentifier(): string
{
    // 返回Eve角色ID作为用户标识
    return (string) $this->eveCharacterId;
}

public function getRoles(): array
{
    // 至少返回ROLE_USER,否则无法访问会员区
    return ['ROLE_USER'];
}

内容的提问来源于stack exchange,提问作者L.H.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:45:33