首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

純 PHP 實現單點登入 (SSO) 系統:基於 Discuz 加密演算法的輕量級方案

     編輯:PHP 綜合知識

設計目標

獨立於業務系統的中央認證服務,支援多子系統免登跳轉,參考 CAS 架構簡化實現,採用 Discuz! 經典 authcode 加密演算法保護傳輸資料。

核心流程

  1. 用戶訪問子系統,未登入 → 重導向至 SSO 登入頁,攜帶 return_url
  2. SSO 驗證帳密成功 → 建立 Session、Cookie,生成加密 Token
  3. 重導向回子系統 passport_login.php,攜帶 userinfo、verify
  4. 子系統驗證 verify、解析 userinfo → 建立本地會話

關鍵實作細節

1. SSO 登入頁

<?php
// login.php
session_start();
$return_url = isset($_GET['return_url']) ? $_GET['return_url'] : '';

if (isset($_POST['username'], $_POST['password'])) {
    // 驗證邏輯(略)
    if ($valid) {
        $_SESSION['user'] = $userinfo;
        setcookie('sso_token', authcode($userinfo, 'ENCODE', $key), time()+3600, '/', '.domain.com');
        header("Location: $return_url?token=" . urlencode(authcode($userinfo, 'ENCODE', $key)));
        exit;
    }
}
// 顯示登入表單,隱藏欄位 return_url
?>

2. 子系統接收登入

<?php
// passport_login.php
$token = $_GET['token'];
$userinfo = authcode($token, 'DECODE', $key);
$verify = $_GET['verify']; // md5($auth . $return_url)

if ($userinfo && md5($key . $return_url) === $verify) {
    // 建立本地會話
    $_SESSION['user'] = $userinfo;
    header('Location: ' . $return_url);
} else {
    die('驗證失敗');
}

3. Discuz authcode 核心函式

<?php
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
    $ckey_length = 4;
    $key = md5($key ? $key : 'YOUR_SECRET_KEY');
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
    $cryptkey = $keya . md5($keya . $keyc);
    $key_length = strlen($cryptkey);
    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
    $string_length = strlen($string);
    $result = '';
    $box = range(0, 255);
    $rndkey = array();
    for ($i = 0; $i <= 255; $i++) $rndkey[$i] = ord($cryptkey[$i % $key_length]);
    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp;
    }
    for ($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a]; $box[$a] = $box[$j]; $box[$j] = $tmp;
        $result .= chr(ord($string[$i]) ^ $box[($box[$a] + $box[$j]) % 256]);
    }
    if ($operation == 'DECODE') {
        if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16))
            return substr($result, 26);
        else return '';
    } else {
        return $keyc . str_replace('=', '', base64_encode($result));
    }
}

安全性考量

  • HTTPS 傳輸:所有跳轉強制 HTTPS,防竊聽
  • Token 時效:authcode 內建時間戳,過期自動失效
  • 域共享 Cookie:.domain.com 實現跨子域單點登入
  • 防重放:verify = md5(key + return_url) 綁定跳轉目標

擴展:單點登出

SSO 提供 logout.php 清除 Session、Cookie,並通知所有已登入子系統(透過 AJAX 輪詢或 Server-Sent Events)同步登出。

現代替代方案

新專案建議採用標準協議:

  • OAuth 2.0 / OpenID Connect:Keycloak、Auth0、自建 IdentityServer
  • SAML 2.0:企業級整合
  • JWT:無狀態認證,適合微服務
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved