别再各写各的登录了!三步吃透PHP(Yii2)接入企业级SSO单点登录"

admin 2026-10-21 23:26:50 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

别再各写各的登录了!三步吃透 PHP (Yii2) 接入企业级 SSO 单点登录

原创

学习与分享
学习与分享

php的自我修养

2026年6月29日 08:30
广东

在小说阅读器读本章

去阅读

商务合作加微信:2230304070

学习与交流:PHP技术自我修养微信群

免费领取phpstorm正版激活码

获取地址:https://web.52shizhan.cn

以下文章正文

research and development

引言:为什么企业级应用需要 SSO?

在企业级应用群落中,孤立的账号体系是研发和用户的噩梦。当公司内部孵化出 OA、CRM、ERP 等多套系统时,如果用户在每个系统都需要重复输入账号密码,不仅体验极差,数据孤立、权限混乱、安全防线难以收拢等痛点也会接踵而至。

SSO(Single Sign-On,单点登录) 正是解决这一痛点的终极方案。它允许用户在多个互信的独立业务系统中,“一次登录,到处通行”。本文将带你从底层机制出发,用 PHP (Yii2 架构) 闭环实现一套工业级的 SSO 接入方案。

一、 核心复盘:SSO 认证究竟是如何运转的?

要接入 SSO,首先必须搞懂它的核心账密置换凭证 —— Ticket(授权码/票据)机制。

我们以经典的中心化认证流为例,当用户访问受保护的业务系统时,其核心步骤如下:

  1. 路由拦截

:用户访问业务系统 B。业务系统发现用户未登录,通过 302 重定向将用户引导至 SSO 认证中心。
2. 凭证分发

:用户在 SSO 中心完成账号密码认证。SSO 中心验证通过后,生成一个短期内有效的随机凭证 Ticket,并带着这个 Ticket 重定向回业务系统 B 预留的回调地址。
3. 后台置换

:业务系统 B 拿到 Ticket 后,在后端(Server to Server)发起请求,向 SSO 中心验证 Ticket 的合法性,并换取当前用户的核心信息(如工号、邮箱、角色)。
4. 本地授信

:业务系统 B 验证无误,将用户信息写入本地 Session/Token,自动完成本地登录,页面正常响应。

二、 实战演练:三步打通 PHP 的 SSO 接入

为了让代码更具规范性和扩展性,我们采用 接口定义  服务实现  控制器调用 的三层架构。

步骤 1:定义统一的 SSO 契约接口

首先,我们定义一个规范的接口。不论未来的 SSO 协议是基于标准的 OIDC、OAuth2 还是自研的 CAS,业务层只需要关心这两个核心动作。

<?php
namespace app\\components\\sso;

interface SSOLoginInterface
{
    /**
     * 获取单点登录中心授权跳转地址
     * @return string 跳转到SSO中心的完整URL
     */
    public function getAuthUrl(): string;

    /**
     * 通过回调获得的 Ticket 换取 SSO 中心的用户信息
     * @param string $ticket 认证票据
     * @return array 用户基础信息数组
     * @throws \\Exception 校验失败时抛出异常
     */
    public function getInfoFromTicket(string $ticket): array;
}

步骤 2:落地具体对接的 SSO 服务类

这里我们模拟一个具体的 CompanySSOService。在实际生产环境下,你可以将这里的 curl 请求替换为你们公司的 SSO 中心真实 API。

<?php
namespaceapp\\components\\sso;

useYii;
useyii\\base\\Component;

class CompanySSOService extends Component implements SSOLoginInterface
{
    public $ssoHost = 'https://sso.company.com';
    public $clientId = 'crm_system_01';
    public $clientSecret = 'sso_secret_key_xxxxxx';

    publicfunction getAuthUrl(): string
    {
        // 告诉SSO中心,认证成功后跳回本地的什么地方
        $callbackUrl = urlencode(Yii::$app->request->getHostInfo() . '/sso/callback');
        return\"{$this->ssoHost}/login?client_id={$this->clientId}&redirect_uri={$callbackUrl}\";
    }

    publicfunction getInfoFromTicket(string $ticket): array
    {
        $apiExhangeUrl = \"{$this->ssoHost}/api/verify-ticket\";

        // 发起后端安全的 Server to Server 请求,杜绝前端伪造
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $apiExhangeUrl);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
            'ticket' => $ticket,
            'client_id' => $this->clientId,
            'client_secret' => $this->clientSecret,
        ]));

        $response = curl_exec($ch);
        curl_close($ch);

        $result = json_decode($response, true);
        if (empty($result) || !isset($result['success']) || !$result['success']) {
            thrownew \\Exception(\"SSO凭证校验失败: \" . ($result['message'] ?? '未知错误'));
        }

        return $result['data']; // 返回格式统一包含: username, email, truename
    }
}

步骤 3:编写控制器,闭环回调登录逻辑

在 Yii2 框架中创建 SsoController。它是整个登录闭环的控制中枢,负责处理Ticket 的接收、新用户的自动常驻同步、以及本地会话的建立。

<?php
namespaceapp\\controllers;

useYii;
useyii\\web\\Controller;
useyii\\web\\BadRequestHttpException;
useapp\\models\\User;
useapp\\components\\sso\\CompanySSOService;

class SsoController extends Controller
{
    /**
     * @return CompanySSOService
     */
    privatefunction getSsoService()
    {
        // 建议在 Yii 的 components 中配置注入,此处快速实例化演示
        returnnew CompanySSOService();
    }

    /**
     * 登录入口触发表
     */
    publicfunction actionLogin()
    {
        // 如果本地已经有登录态,直接去后台首页
        if (!Yii::$app->user->isGuest) {
            return$this->redirect(['/dashboard']);
        }

        // 渲染登录引导页,将前端登录按钮的链接指向 SSO 授权页
        return$this->render('login', [
            'ssoUrl' => $this->getSsoService()->getAuthUrl()
        ]);
    }

    /**
     * SSO 中心认证通过后的核心回调接口
     */
    publicfunction actionCallback()
    {
        $ticket = Yii::$app->request->get('ticket');

        if (empty($ticket)) {
            thrownew BadRequestHttpException('授权失败:缺失关键 ticket 参数');
        }

        try {
            // 1. 去 SSO 中心洗白,拿回用户的真实资料
            $ssoUser = $this->getSsoService()->getInfoFromTicket($ticket);

            if (empty($ssoUser['username'])) {
                return$this->renderError('SSO 授信中心返回的用户数据不完整');
            }

            // 2. 统一格式规整(防止大小写引发的账号双胞胎问题)
            $username = strtolower(trim($ssoUser['username']));

            // 3. 检索本地账号仓库,不存在则自动激活“影子上游账号”
            $user = User::find()->where(['username' => $username])->one();

            if (!$user) {
                $user = new User();
                $user->username = $username;
                $user->email = $ssoUser['email'] ?? \"{$username}@company.com\";
                $user->realname = $ssoUser['truename'] ?? '新员工';
                $user->role = User::ROLE_MEMBER; // 默认赋予普通成员权限
                $user->status = User::STATUS_ACTIVE;

                if (!$user->save()) {
                    thrownew \\Exception(\"本地同步创建用户失败:\" . json_encode($user->getErrors()));
                }
            }

            // 4. 正式签发本地登录会话(Session/Cookie 保持 30 天)
            Yii::$app->user->login($user, 3600 * 24 * 30);

            // 5. 顺滑流转至系统内部首页
            return$this->redirect(['/dashboard']);

        } catch (\\Exception $e) {
            Yii::error(\"SSO登录异常挂起: \" . $e->getMessage(), __METHOD__);
            return$this->renderError($e->getMessage());
        }
    }

    privatefunction renderError($msg)
    {
        return$this->render('error', [
            'message' => $msg,
            'retryUrl' => $this->getSsoService()->getAuthUrl()
        ]);
    }
}

三、 架构师避坑:企业级 SSO 的三大防御工事

单点登录一旦被攻破,意味着黑客拿到了进入公司所有系统的万能钥匙。在生产环境中部署时,必须牢记以下三大安全增强准则:

1. Ticket 防重放与防篡改

  • 一次性失效

:Ticket 在设计时,必须在 SSO 服务端执行 “阅后即焚” 策略。即任何一个 Ticket 只要被业务系统验证过一次,无论成功与否,在 Redis 中都必须立刻被判定为作废。
* 时效极短化

:Ticket 只是用于置换 Token 的临时媒介,有效期通常应该限制在 30 秒 ~ 1 分钟 之内。

2. 用户动态映射与状态同步

  • 唯一标识锚定

:在多系统间匹配用户时,不要直接依赖极易被修改的手机号或用户名。推荐使用全局唯一的员工工号(Staff ID)或不可变的专属 UID 作为主键映射绑定。
* 状态熔断

:如果员工在 SSO 中心被 HR 标记为离职或禁用,当其再次访问业务系统时,业务系统如果直接读取本地 Session,会导致离职员工仍有系统权限。因此,本地 Session 存活期不宜过长,或者业务系统在核心节点需定期(如每 5 分钟)异步校验一次 SSO 态的有效性。

3. 全链路安全防御

  • 加签防伪造

:SSO 中心和各业务系统之间,推荐使用基于 RSA 签名 或 JWT 非对称加密 的算法。即使 Ticket 被拦截,黑客在没有私钥的情况下也无法伪造用户信息。
* 全站 HTTPS

:由于全链路都在重定向传输敏感凭证,严禁在 HTTP 纯明文环境下裸奔,所有域名必须无条件强制开启 HTTPS。

四、 总结

通过上述的逻辑重构与代码补充,我们可以发现,PHP 在接入 SSO 时拥有天然的高效和灵活性。只要我们卡死回调接口,做好 Ticket 的后台二次验证和账号的动态同步,就可以快速让任何一个新旧 PHP 项目无缝融入到公司的全局统一账号生态中。

原文链接:

以上就是本篇文章的全部内容,希望各位程序员们努力提升个人技术。最后,小编温馨提示:每天阅读5分钟,每天学习一点点,每天进步一点点。

点个赞

再走吧


别再各写各的登录了!三步吃透PHP(Yii2)接入企业级SSO单点登录" 网络安全文章

别再各写各的登录了!三步吃透PHP(Yii2)接入企业级SSO单点登录"

本文详细讲解PHP(Yii2)框架接入企业级SSO单点登录的完整方案。核心内容包括SSO的Ticket认证机制、三层架构实现(接口定义-服务实现-控制器调用),以及企业级安全防护要点。文章提供了完整的代码示例,涵盖认证流程、用户信息同步和会话管理,并强调Ticket防重放、用户状态同步、全链路加密等安全准则。
汽车网络安全与功能安全活动之间的关系" 网络安全文章

汽车网络安全与功能安全活动之间的关系"

该文档为汽车网络安全活动宣传内容,主要介绍2026年6月29日在上海举办的汽车网络安全与功能安全相关活动。文档包含大量图片占位符和推广信息,重点引导用户加入技术交流群、下载行业白皮书及PPT资料,并附有多篇汽车功能安全与信息安全相关的解决方案文档链接。
实战复盘|从弱口令到Furion定时任务RCE的漏洞链" 网络安全文章

实战复盘|从弱口令到Furion定时任务RCE的漏洞链"

该文档详细记录了作者在一次微信小程序渗透测试中发现后端Swagger页面存在弱口令漏洞,通过横向扩展发现同一CMS系统其他子域名存在相同弱口令,进而利用Furion框架定时任务功能实现远程代码执行的全过程。文档展示了从信息收集、漏洞验证到利用AI辅助编写攻击代码、通过HTTP外带数据的关键步骤,并总结了弱口令横向测试、出网验证等实战经验。
nonono.不要使用kill-9" 网络安全文章

nonono.不要使用kill-9"

本文详细解释了Linux中kill命令的工作原理,强调kill-9(SIGKILL)的危险性,因为它会立即终止进程而不允许其进行清理操作,可能导致数据丢失或系统不一致。文章建议优先使用SIGTERM(-15)和SIGINT(-2)等温和信号,并提供了分步骤的进程终止策略,仅在进程完全无响应时才将SIGKILL作为最后手段。
评论:0   参与:  0