您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# PHP中实现SSO单点登录的方法
## 引言
单点登录(Single Sign-On,简称SSO)是现代Web应用中的重要功能,它允许用户通过一次认证即可访问多个相互信任的系统。本文将深入探讨在PHP环境中实现SSO的多种技术方案,包含完整代码示例和最佳实践。
## 一、SSO核心原理
### 1.1 基本概念
SSO系统通常包含三个核心组件:
- **认证中心(IdP)**:负责用户身份验证
- **服务提供方(SP)**:具体业务应用
- **令牌机制**:传递认证状态的媒介
### 1.2 工作流程
```mermaid
sequenceDiagram
User->>SP: 访问应用
SP->>IdP: 重定向到认证中心
IdP->>User: 展示登录页
User->>IdP: 提交凭证
IdP->>SP: 返回含令牌的跳转
SP->>IdP: 验证令牌有效性
IdP->>SP: 返回用户信息
SP->>User: 授权访问
// 使用Redis作为共享存储
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 生成全局SessionID
function generateSSOToken() {
return bin2hex(random_bytes(32));
}
// 存储用户会话
$ssoToken = generateSSOToken();
$redis->setex("sso:{$ssoToken}", 3600, json_encode([
'user_id' => 123,
'username' => 'example_user'
]));
// 主域名登录后设置Cookie
setcookie(
'sso_token',
$ssoToken,
time() + 3600,
'/',
'.example.com', // 主域名
true, // 仅HTTPS
true // HttpOnly
);
// 子站点验证逻辑
if(isset($_COOKIE['sso_token'])) {
$userData = $redis->get("sso:{$_COOKIE['sso_token']}");
if($userData) {
$_SESSION['user'] = json_decode($userData, true);
}
}
use Firebase\JWT\JWT;
$secretKey = 'your-256-bit-secret';
$payload = [
'iss' => 'https://sso.example.com',
'aud' => 'https://app1.example.com',
'iat' => time(),
'exp' => time() + 3600,
'sub' => 'user123',
'name' => 'John Doe'
];
$jwt = JWT::encode($payload, $secretKey, 'HS256');
// SP端验证中间件
function authenticateJWT($jwt) {
try {
$decoded = JWT::decode($jwt, $secretKey, ['HS256']);
return (array)$decoded;
} catch (Exception $e) {
header('Location: https://sso.example.com/login?redirect='.urlencode($_SERVER['REQUEST_URI']));
exit;
}
}
// 使用league/oauth2-server库
$server = new \League\OAuth2\Server\AuthorizationServer(
new ClientRepository(),
new AccessTokenRepository(),
new ScopeRepository(),
'file://path/to/private.key',
'file://path/to/public.key'
);
// 启用授权码模式
$server->enableGrantType(
new \League\OAuth2\Server\Grant\AuthCodeGrant(
new AuthCodeRepository(),
new RefreshTokenRepository(),
new \DateInterval('PT10M') // 授权码有效期
),
new \DateInterval('PT1H') // 访问令牌有效期
);
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => 'your-client-id',
'clientSecret' => 'your-client-secret',
'redirectUri' => 'https://your-app.com/callback',
'urlAuthorize' => 'https://sso.example.com/oauth/authorize',
'urlAccessToken' => 'https://sso.example.com/oauth/token',
'urlResourceOwnerDetails' => 'https://sso.example.com/oauth/userinfo'
]);
// 获取授权URL
$authUrl = $provider->getAuthorizationUrl();
$_SESSION['oauth2state'] = $provider->getState();
header('Location: '.$authUrl);
<!-- cas-server-webapp配置示例 -->
<bean id="serviceRegistryDao" class="org.jasig.cas.services.JsonServiceRegistryDao"
c:configDirectory="${cas.serviceRegistry.config.location}" />
<bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
<constructor-arg>
<map>
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
</map>
</constructor-arg>
</bean>
require_once 'CAS.php';
phpCAS::client(CAS_VERSION_2_0, 'sso.example.com', 443, '/cas');
// 强制SSL验证
phpCAS::setCasServerCACert('/path/to/cas_cert.pem');
if (!phpCAS::isAuthenticated()) {
phpCAS::forceAuthentication();
} else {
$user = phpCAS::getUser();
$attributes = phpCAS::getAttributes();
}
if (empty($_GET['state']) || $_GET['state'] !== $_SESSION['oauth2state']) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
}
$payload['fingerprint'] = hash('sha256',
$_SERVER['HTTP_USER_AGENT'] .
$_SERVER['REMOTE_ADDR']
);
// JWT验证缓存
function verifyJWTWithCache($jwt) {
$cacheKey = 'jwt_'.hash('sha256', $jwt);
if($cached = apcu_fetch($cacheKey)) {
return $cached;
}
$decoded = JWT::decode($jwt, $secretKey, ['HS256']);
apcu_store($cacheKey, $decoded, 300);
return $decoded;
}
+-----------------+
| 认证中心(IdP) |
| PHP + MySQL |
+-------+---------+
|
+------------------------+------------------+
| | |
+--------+---------+ +----------+--------+ +----+--------+
| 电商系统(SP) | | CRM系统(SP) | | CMS系统(SP) |
| Laravel + Redis | | ThinkPHP + JWT | | WordPress |
+------------------+ +-------------------+ +-------------+
sequenceDiagram
participant User
participant SP1
participant IdP
participant SP2
User->>SP1: 访问系统A
SP1->>IdP: 检查未登录(302)
User->>IdP: 登录页面
IdP->>User: 设置SSO Cookie
User->>SP1: 带令牌重定向
SP1->>IdP: 验证令牌
IdP->>SP1: 返回用户数据
User->>SP2: 访问系统B
SP2->>IdP: 检查已有会话
IdP->>SP2: 直接返回用户数据
// 使用CORS中间件
header('Access-Control-Allow-Origin: https://sso.example.com');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
// 处理App深度链接
function handleSSORedirect() {
if(window.location.search.includes('sso_token=')) {
const token = new URLSearchParams(window.location.search).get('sso_token');
window.ReactNativeWebView?.postMessage(JSON.stringify({
type: 'SSO_TOKEN',
token: token
}));
}
}
实现SSO需要根据具体场景选择合适的技术方案。对于PHP生态系统,建议: 1. 内部系统使用Session共享方案 2. 对外服务采用OAuth2.0协议 3. 教育机构等传统环境可考虑CAS
随着Web技术的发展,未来SSO实现将更加依赖标准化协议(如OpenID Connect)和云原生解决方案。开发者应持续关注安全更新和性能优化实践。
本文示例代码已测试通过PHP 8.1环境,完整实现请参考各方案官方文档。 “`
注:实际部署时请务必: 1. 使用HTTPS加密所有通信 2. 定期轮换加密密钥 3. 实现完备的日志审计系统
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。