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

如何实现两个Symfony5站点间跳转的跨站自动登录?

实现方案

这个需求完全可实现,你之前的方案失效有两个核心原因:一是明文URL传参无任何安全校验,任何人修改参数即可登录任意账号;二是Symfony 5.4的Security认证组件默认仅会拦截防火墙配置的指定检查路径的请求,普通GET请求带参数不会触发你写在UserAuthenticator里的认证逻辑。
推荐使用带签名的一次性短时效令牌方案实现,安全性高且完全符合Symfony的认证流程,具体步骤如下:

步骤1:配置跨站共享密钥

在两个站点的.env文件中配置完全一致的随机签名密钥,密钥长度建议32位以上,不要硬编码在业务代码中:

# 两个站点该值必须完全相同
CROSS_SITE_LOGIN_SECRET=这里替换为你自己生成的随机长字符串

步骤2:站点1生成安全跳转链接

不要直接在URL中明文传递用户姓名,在站点1的控制器中生成带签名、带过期时间的一次性令牌,再拼接触发跳转的链接:

// 站点1中生成跳转链接的示例代码
public function buildSsoRedirectUrl(User $selectedUser): string
{
    $secret = $this->getParameter('env(CROSS_SITE_LOGIN_SECRET)');
    // 令牌载荷,仅存必要信息
    $payload = [
        'firstname' => $selectedUser->getFirstname(),
        'lastname' => $selectedUser->getLastname(),
        'exp' => time() + 30, // 令牌30秒过期,仅给跳转瞬间使用
        'nonce' => bin2hex(random_bytes(8)) // 随机串用于防重放攻击
    ];

    // 用共享密钥对载荷生成签名,防止参数被篡改
    $payloadJson = json_encode($payload);
    $sign = hash_hmac('sha256', $payloadJson, $secret);
    $token = base64_encode($payloadJson . '|' . $sign);

    return 'https://myWebsite2.com/sso-login?token=' . urlencode($token);
}

前端模板中正常使用该生成的链接作为href属性值,保留target="_blank"即可。

步骤3:站点2实现SSO登录接口

不要在原有表单登录的UserAuthenticator中硬加判断,单独新增一个SSO专用的登录路由处理逻辑,避免影响正常登录流程:

  1. 首先新增路由配置:
# 站点2 config/routes.yaml
sso_entry:
  path: /sso-login
  controller: App\Controller\SecurityController::ssoLogin
  1. 编写控制器中的登录处理逻辑:
// 站点2 src/Controller/SecurityController.php
public function ssoLogin(
    Request $request,
    UserAuthenticatorInterface $authenticator,
    UserProviderInterface $userProvider,
    CacheInterface $cache
): Response {
    $token = $request->query->get('token');
    if (!$token) throw new AccessDeniedHttpException('登录凭证缺失');

    $secret = $this->getParameter('env(CROSS_SITE_LOGIN_SECRET)');
    $decodedToken = base64_decode($token);
    if (!str_contains($decodedToken, '|')) throw new AccessDeniedHttpException('凭证格式非法');

    // 拆分载荷和签名
    [$payloadJson, $sign] = explode('|', $decodedToken, 2);
    // 校验签名,确认参数未被篡改
    $expectedSign = hash_hmac('sha256', $payloadJson, $secret);
    if (!hash_equals($expectedSign, $sign)) throw new AccessDeniedHttpException('凭证校验失败');

    $payload = json_decode($payloadJson, true);
    // 校验令牌是否过期
    if ($payload['exp'] < time()) throw new AccessDeniedHttpException('登录凭证已过期');
    // 可选:校验nonce防止重放,需要缓存组件支持
    $nonceKey = 'sso_nonce_' . $payload['nonce'];
    if ($cache->has($nonceKey)) throw new AccessDeniedHttpException('凭证已被使用');
    $cache->set($nonceKey, 1, 60);

    // 按你自己的业务逻辑查找对应用户,不存在可按需自动创建或抛错
    $user = $userProvider->findOneBy([
        'firstname' => $payload['firstname'],
        'lastname' => $payload['lastname']
    ]);
    if (!$user) throw new AccessDeniedHttpException('对应用户不存在');

    // 触发Symfony官方登录流程,写入安全会话
    return $authenticator->authenticateUserAndHandleSuccess(
        $user,
        $request,
        $this->get(Security\Http\Authenticator\AuthenticatorInterface::class), // 替换为你项目中实际使用的认证器服务
        'main' // 替换为你security.yaml中配置的对应防火墙名称
    );
}

注意事项

  • 令牌有效期不要设置过长,10-60秒足够覆盖点击跳转的网络耗时,降低令牌被盗用的风险
  • 不要跳过签名校验步骤,否则和明文传参没有区别
  • 如果两个站点属于同一根域名,可按需调整cookie作用域实现会话共享,但新标签页场景下使用上述一次性令牌方案已完全满足需求
  • 如果你需要更高安全等级,可将nonce校验逻辑替换为数据库/Redis存储,彻底防止令牌重放

内容的提问来源于stack exchange,提问作者team.dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 05:12:27