PHP项目如何实现统一认证?

wen java案例 2

本文目录导读:

PHP项目如何实现统一认证?

  1. 方案一:基于 JWT(JSON Web Token) + 共享密钥/开放密钥(最简单、最常用)
  2. 方案二:基于 OAuth2 / OpenID Connect(OIDC) + 独立认证服务器(最标准、最安全)
  3. 方案三:基于共享 Session(适用于同一域名下的子站)
  4. 总结与选择建议

在PHP项目中实现统一认证(SSO,Single Sign-On),核心思路是建立一个独立的认证中心,所有应用(子站)都通过该中心来验证用户身份,而不是各自维护一套登录逻辑。

以下是几种主流且实用的实现方案,按推荐程度和复杂度排序:

基于 JWT(JSON Web Token) + 共享密钥/开放密钥(最简单、最常用)

这是目前中小型PHP项目最流行、最易于实现的方案,不需要独立的认证服务器,只需要一个共享的加密密钥

原理:

  1. 认证中心(主站): 用户在此登录,生成JWT,JWT中包含用户ID、角色、过期时间等信息,并用密钥签名。
  2. 子站(子应用): 收到JWT后,用相同的密钥验证签名是否有效、是否过期。
  3. 所有站点共享同一个密钥(或使用非对称加密,认证中心持有私钥,子站持有公钥)。

PHP实现步骤(使用 firebase/php-jwt 库):

  1. 安装库(在认证中心和所有子站):

    composer require firebase/php-jwt
  2. 认证中心(登录接口 login.php):

    <?php
    require_once 'vendor/autoload.php';
    use \Firebase\JWT\JWT;
    $key = "your-256-bit-secret-key-here"; // 重要:所有站点共享此密钥
    $payload = [
        'iss' => 'your-main-domain.com', // 签发者
        'iat' => time(),                 // 签发时间
        'exp' => time() + 3600,          // 过期时间(1小时后)
        'user_id' => 123,
        'username' => '张三',
        'role' => 'admin'
    ];
    $jwt = JWT::encode($payload, $key, 'HS256');
    // 将 $jwt 返回给前端(例如存入Cookie,注意设置SameSite=None, Secure)
    setcookie('token', $jwt, time()+3600, '/', '.your-domain.com', true, true);
    header('Location: https://sub.your-domain.com/dashboard');
  3. 子站(验证接口 verify.php):

    <?php
    require_once 'vendor/autoload.php';
    use \Firebase\JWT\JWT;
    use \Firebase\JWT\Key;
    $key = "your-256-bit-secret-key-here"; // 与认证中心相同
    if (isset($_COOKIE['token'])) {
        try {
            $token = $_COOKIE['token'];
            $decoded = JWT::decode($token, new Key($key, 'HS256'));
            $user_id = $decoded->user_id;
            // 验证通过,可以安全使用用户信息
            echo "欢迎,用户ID: " . $user_id;
        } catch (\Exception $e) {
            // Token无效或过期,重定向到认证中心
            header('Location: https://auth.your-domain.com/login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
            exit;
        }
    } else {
        // 未登录,重定向到认证中心
        header('Location: https://auth.your-domain.com/login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
        exit;
    }

优缺点:

  • 优点: 实现简单,无需独立数据库或服务,性能好。
  • 缺点: 密钥泄露风险(所有站点必须妥善保管密钥,不能放在代码仓库),无法实现立即登出(除非维护黑名单,增加复杂度),子站间用户会话状态独立。

基于 OAuth2 / OpenID Connect(OIDC) + 独立认证服务器(最标准、最安全)

这是企业级应用的推荐方案,也是Google、GitHub等第三方登录的标准协议,你需要搭建或使用现成的认证服务器(如Keycloak、Ory Hydra、自定义Laravel Passport)。

核心思想: 认证中心(授权服务器)负责登录,生成一个授权码Access Token,子站拿着这个Token去认证中心换取用户信息,认证中心是所有用户信息的唯一来源。

PHP实现(以Laravel Passport为例,但原理通用):

  1. 搭建认证服务器(Auth Server): 使用Laravel + Passport(或PHP League OAuth2 Server)。

    • 它负责提供登录界面、发放Access Token、提供/userinfo接口。
  2. 配置子站(Client):

    • 每个子站需要在认证服务器上注册(获取client_idclient_secret)。
    • 子站配置重定向URL(例如https://sub1.com/callback)。
  3. 登录流程(Authorization Code Grant):

    • 步骤1(重定向): 用户访问子站sub1.com,未登录 -> 重定向到认证服务器: https://auth-server.com/oauth/authorize?client_id=SUB1_CLIENT_ID&redirect_uri=https://sub1.com/callback&response_type=code&state=xyz
    • 步骤2(用户登录): 用户在认证服务器输入密码。
    • 步骤3(回调): 认证服务器重定向回子站的callback.php,携带code
    • 步骤4(换Token): 子站后端 callback.phpcode + client_secret 向认证服务器请求Access Token。
    • 步骤5(获取用户信息): 子站用Access Token请求https://auth-server.com/api/user 获取用户基本信息(ID、邮箱等)。
    • 步骤6(建立会话): 子站将获取到的用户信息存入自己的Session或Cookie(如JWT),完成登录。

优点:

  • 极其安全: 用户密码只在认证服务器输入,子站拿不到密码。
  • 标准协议: 可对接任何支持OAuth2的PHP库或第三方服务。
  • 登出方便: 认证服务器可以维护一个Token黑名单,或采用refresh_token机制,实现全局登出。

缺点:

  • 架构复杂: 需要维护一套独立的认证服务器,或引入Keycloak等重量级软件。
  • 性能开销: 每次登录都需要网络请求到认证服务器。

基于共享 Session(适用于同一域名下的子站)

如果所有子站都在同一个主域名下(app1.example.comapp2.example.com),并且共享同一个Redis或数据库Session存储,可以直接使用PHP内置的Session。

原理:

  1. 所有PHP应用配置为使用同一个Redis服务器存储Session。
  2. 登录成功后,Session数据写入Redis。
  3. 子站只需更改session.cookie_domain.example.com,所有子站都能读取到相同的PHPSESSID
  4. 解析PHPSESSID后,从Redis读取用户数据。

PHP配置(php.ini或代码中):

session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=password"
session.cookie_domain = ".example.com"
session.cookie_secure = 1
session.cookie_samesite = "None"

优点: 实现最简单,完美利用现有Session机制。 缺点: 子站必须是相同域名(.example.com,无法跨完全不同的域名(如.com.cn);所有子站代码必须能访问同一个Redis,扩展性受限;与语言/框架绑定较紧。


总结与选择建议

方案 复杂度 安全性 跨域名支持 是否推荐 适用场景
JWT + 共享密钥 中(密钥易泄露) (跨任何域名) 强烈推荐 中小型项目、微服务、API认证,简单快速。
OAuth2 / OIDC (行业标准) (完全隔离) 企业必选 大型系统、需要对接第三方登录、高安全要求。
共享Session 中(依赖Redis安全) (同域名) 特定场景 所有子站在同一主域名下的简单内部系统。

直接建议:

  • 如果只是几个简单的公司内部系统,且都在 .company.com 下, 可以直接用方案三(共享Session),最快。
  • 如果系统需要提供给外部用户,或子站域名不同,或需要对接微信/支付宝登录, 首选方案二(OAuth2),可以使用现成的开源项目如 Keycloak,它自带管理界面,省去自己写认证服务器的麻烦。
  • 如果项目规模不大、追求快速开发, 方案一(JWT) 足够用,但注意:务必为JWT设置较短的过期时间(如15-30分钟),并结合refresh_token机制(可放在HTTP Only的Cookie里)来平衡安全与体验。

抱歉,评论功能暂时关闭!