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

Symfony 3.4如何在登出前判断登录类型并实现SAML重定向?

在Symfony 3.4中处理SAML前置登出重定向

Symfony 3.4没有5.1+那种内置的"登出前"钩子,但我们可以通过事件拦截或者自定义登出控制器来实现需求——先跳去Azure AD完成单点登出,再执行本地登出。下面是具体的实现方案:

核心思路

  1. 识别当前登出的用户是否是SAML认证的;
  2. 如果是SAML用户,先重定向到Azure AD的单点登出(SLO)地址,同时指定回调路由;
  3. 当Azure AD完成登出后,跳回我们的回调路由,在那里执行本地登出操作;
  4. 非SAML用户直接走默认登出流程。

步骤1:识别SAML用户

你有两种方式判断用户是否通过SAML登录:

  • 方式A:通过Token类型判断(推荐,如果你用第三方SAML bundle如OneLogin)
    比如OneLogin的SAML bundle会生成SamlToken类型的认证Token,直接判断Token实例即可:
    $token = $this->get('security.token_storage')->getToken();
    if ($token instanceof \OneLogin\SamlBundle\Security\Core\Authentication\Token\SamlToken) {
        // 是SAML用户
    }
    
  • 方式B:通过用户实体字段判断
    如果你的用户表有auth_type字段(存储local/oauth2/ldap/saml),直接从用户对象获取:
    $user = $token->getUser();
    if ($user instanceof UserInterface && $user->getAuthType() === 'saml') {
        // 是SAML用户
    }
    

步骤2:拦截登出请求(事件订阅器方案)

用kernel.request事件拦截登出请求,优先级要高于Symfony默认的LogoutListener(优先级64),确保我们的逻辑先执行。

创建事件订阅器

// src/AppBundle/EventSubscriber/SamlLogoutSubscriber.php
namespace AppBundle\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use OneLogin\Saml2\Auth;

class SamlLogoutSubscriber implements EventSubscriberInterface
{
    private $tokenStorage;
    private $router;
    private $samlSettings;

    public function __construct(TokenStorageInterface $tokenStorage, RouterInterface $router, array $samlSettings)
    {
        $this->tokenStorage = $tokenStorage;
        $this->router = $router;
        $this->samlSettings = $samlSettings;
    }

    public static function getSubscribedEvents()
    {
        return [
            // 优先级高于默认LogoutListener的64
            KernelEvents::REQUEST => ['handleSamlLogout', 70],
        ];
    }

    public function handleSamlLogout(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        
        // 只处理登出请求(根据你的security.yml里的logout路径调整)
        if ($request->getPathInfo() !== '/logout') {
            return;
        }

        $token = $this->tokenStorage->getToken();
        // 替换成你的SAML Token判断逻辑
        if (!$token instanceof \OneLogin\SamlBundle\Security\Core\Authentication\Token\SamlToken) {
            return; // 非SAML用户,走默认流程
        }

        // 构造Azure AD的单点登出URL,带上回调地址
        $samlAuth = new Auth($this->samlSettings);
        $returnTo = $this->router->generate('finish_saml_logout', [], RouterInterface::ABSOLUTE_URL);
        $logoutUrl = $samlAuth->logout($returnTo);

        // 重定向到Azure AD完成登出
        $event->setResponse(new RedirectResponse($logoutUrl));
    }
}

注册订阅器

在app/config/services.yml里添加:

services:
    app.saml_logout_subscriber:
        class: AppBundle\EventSubscriber\SamlLogoutSubscriber
        arguments:
            - '@security.token_storage'
            - '@router'
            - '%onelogin_saml.settings%' # 你的SAML bundle配置参数
        tags:
            - { name: kernel.event_subscriber }

步骤3:实现回调路由(完成本地登出)

创建一个路由,用来接收Azure AD的登出回调,在这里执行本地登出操作:

添加路由

# app/config/routing.yml
finish_saml_logout:
    path: /finish-saml-logout
    defaults: { _controller: AppBundle:Security:finishSamlLogout }

编写控制器逻辑

// src/AppBundle/Controller/SecurityController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;

class SecurityController extends Controller
{
    public function finishSamlLogoutAction(Request $request)
    {
        // 清除认证Token
        $this->get('security.token_storage')->setToken(null);
        
        // 销毁session
        $request->getSession()->invalidate();

        // 重定向到登录页或首页
        return new RedirectResponse($this->generateUrl('homepage'));
    }
}

步骤4:配置Azure AD

记得在Azure AD的SAML应用配置里,把/finish-saml-logout的完整URL添加到允许的回调地址列表中,否则Azure AD不会跳回你的应用。

备选方案:自定义登出控制器

如果你不想用事件订阅器,也可以直接把默认的/logout路由指向自定义控制器,在控制器里做判断:

修改路由

# app/config/routing.yml
logout:
    path: /logout
    defaults: { _controller: AppBundle:Security:logout }

控制器逻辑

public function logoutAction(Request $request)
{
    $token = $this->get('security.token_storage')->getToken();
    if ($token instanceof \OneLogin\SamlBundle\Security\Core\Authentication\Token\SamlToken) {
        // 跳去Azure AD登出
        $samlAuth = new Auth($this->getParameter('onelogin_saml.settings'));
        $returnTo = $this->generateUrl('finish_saml_logout', [], RouterInterface::ABSOLUTE_URL);
        return new RedirectResponse($samlAuth->logout($returnTo));
    }

    // 非SAML用户执行默认登出
    $this->get('security.token_storage')->setToken(null);
    $request->getSession()->invalidate();
    return new RedirectResponse($this->generateUrl('login'));
}

这种方式更直接,但需要自己处理非SAML用户的登出逻辑,适合需求简单的场景。


内容的提问来源于stack exchange,提问作者Kolovos Konstantinos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 12:48:13