SSO单点登录-简单实现
一、二级域名之间的单点登录
不需要用到JSONP 或者 p3p 协议,直接使用 COOKIE 就行了,因为顶级域名相同就能实现 COOKIE 共享。
例如有两个项目,域名分别是 www.site1.com 和 mall.site1.com,分别对应的项目目录是 /site1/p3p 和 /site1_origin
site1 的登陆页面 /site1/p3p/login.php
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>site1-login</title> </head> <?php header('content-type:text/html;charset=utf-8'); session_start(); $user = isset($_SESSION['username']) ? $_SESSION['username'] : '游客'; echo '你好, '.$user; if($user != '游客') { echo ' | <a href="logout.php">退出</a><br /><br />'; } else { echo '<br /><br />'; } ?> <body> <form action="" method="post"> <table> <tr> <td>登录名:</td> <td><input type="input" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td colspan="2"><input type="submit" value="登陆"></td> </tr> </table> </form> </body> </html> <?php if(isset($_POST['username'])) { $username = htmlentities($_POST['username']); $password = md5(htmlentities($_POST['password'])); // 设置同域 COOKIE setcookie('username', $username, time() + 3600, '/', 'site1.com'); $_SESSION['username'] = $username; ?> <script>window.location = "index.php";</script> <?php } ?>
通过 setcookie() 的第五个参数来设置 COOKIE 域,当设置为 'site1.com' 时,在 www.site1.com 和 mall.site1.com 中同时会生成 COOKIE。
mall.site1.com 的 index.php 页面用于查看 COOKIE:
<?php header('content-type:text/html;charset=utf-8'); session_start(); if(isset($_COOKIE['username'])) { $user = $_COOKIE['username']; $_SESSION['username'] = $user; } else { $user = '游客'; } echo '你好, '.$user;
二、跨域的单点登录
例如有四个项目,域名分别是 www.site1.com 、mall.site1.com、www.site2.com、www.sso.com,分别对应的项目目录是 /site1/p3p 、 /site1_origin、/site2/p3p、/sso
如图:
www.site1.com 和 www.site2.com 是两个不同的域,mall.site1.com 是 site1.com 的一个二级域名,www.sso.com 是处理 sso 单点登录单独设置的一个服务。
原理是当用户在 www.site1.com 或 www.site2.com 进行登陆或者注销时,生成相应的参数,并且重定向到 www.sso.com 对所有相关的站点借助 <script> 发送 HTTP 请求,添加或者删除 COOKIE,以达到同时登陆同时注销的目的,再跳转回原站点。
site1 的登录页面 /site1/p3p/login.php:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>site1-login</title> </head> <?php header('content-type:text/html;charset=utf-8'); session_start(); $user = isset($_SESSION['username']) ? $_SESSION['username'] : '游客'; $welcome = $user == '游客' ? $user : '<a href="index.php">'.$user.'</a>'; echo '你好, '.$welcome; if($user != '游客') { echo ' | <a href="logout.php">退出</a><br /><br />'; } else { echo '<br /><br />'; } ?> <body> <form action="" method="post"> <table> <tr> <td>登录名:</td> <td><input type="input" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td colspan="2"><input type="submit" value="登陆"></td> </tr> </table> </form> </body> </html> <?php if(isset($_POST['username'])) { $username = htmlentities($_POST['username']); $password = md5(htmlentities($_POST['password'])); // 设置同域 COOKIE setcookie('username', $username, time() + 3600, '/', 'site1.com'); $_SESSION['username'] = $username; $salt = 'sso_example_'.strtotime(date('Y-m-d H')); $token = md5($salt); // 跳转 header("Location:http://www.sso.com/set_cookie.php?username=$username&token=$token&from=".urlencode('http://'.$_SERVER['SERVER_NAME'])."&operate=1"); } ?>
当 operate 为 1 时为登陆,为 2 时为注销。
site1 的 COOKIE 处理页面 /site1/p3p/set_cookie.php
<?php session_start(); $salt = 'sso_example_'.strtotime(date('Y-m-d H')); if($_GET['token'] == md5($salt) && isset($_GET['username']) && $_GET['username'] != '') { // 添加 SESSION if(1 == $_GET['operate']) { $username = trim(htmlentities($_GET['username'])); $_SESSION['username'] = $username; setcookie('username', $username, time() + 3600, '/', 'site1.com'); } else { // 删除 SESSION session_unset(); session_destroy(); // 删除 COOKIE setcookie('username', false, time() - 1, '/', 'site1.com'); if(isset($_COOKIE[session_name()])) { setcookie(session_name(), '', time() - 1, '/', 'site1.com'); } } }
site1 的注销页面 site1/p3p/logout.php
<?php header('content-type:text/html;charset=utf-8'); session_start(); session_unset(); session_destroy(); $salt = 'sso_example_'.strtotime(date('Y-m-d H')); $token = md5($salt); $username = $_COOKIE['username']; if(isset($_COOKIE[session_name()])) { setcookie(session_name(), '', time() - 1, '/', 'site1.com'); } setcookie('username', false, time() - 1, '/', 'site1.com'); echo '退出成功'; // 跳转 header("Location:http://www.sso.com/set_cookie.php?username=$username&token=$token&from=".urlencode('http://'.$_SERVER['SERVER_NAME'])."&operate=2"); ?>
site1 的主页,用于显示用户的登陆情况 /site1/p3p/index.php
<?php header('content-type:text/html;charset=utf-8'); session_start(); if(isset($_COOKIE['username'])) { $user = $_COOKIE['username']; $_SESSION['username'] = $user; } else { $user = '游客'; } echo '你好, '.$user; if($user != '游客') { echo ' | <a href="login.php">返回登录页</a>'; echo ' | <a href="logout.php">退出</a>'; } else { echo ' | <a href="login.php">登陆</a>'; }
mll.site1.com 的主页,查看用户登录或者注销情况 /site1_origin/index.php
<?php header('content-type:text/html;charset=utf-8'); session_start(); if(isset($_COOKIE['username'])) { $user = $_COOKIE['username']; $_SESSION['username'] = $user; } else { $user = '游客'; } echo '你好, '.$user;
-------------------------------------------------------------------------------------
www.sso.com 借助 script 标签对所有站点发出 HTTP 请求。/sso/set_cookie.php
<?php $username = trim(htmlentities($_GET['username'])); $token = $_GET['token']; $from = $_GET['from']; $operate = $_GET['operate']; // 同时登陆、注销的站点 $web_sites = array('www.site1.com', 'www.site2.com'); foreach($web_sites as $sites) { ?> <script src="http://<?php echo $sites;?>/p3p/set_cookie.php?username=<?php echo $username;?>&token=<?php echo $token;?>&from=<?php echo urlencode($from);?>&operate=<?php echo $operate;?>"></script> <?php } $from = urldecode($from); ?> <script> window.location = "<?php echo $from;?>/p3p/<?php if(1 == $operate) { echo 'index.php';}else { echo 'login.php';} ?>"; </script>
----------------------------------------------------------------------------------
site2 登陆页面 /site2/p3p/login.php
site2 注销页面 /site2/p3p/logout.php
site2 COOKIE 处理页面 /site2/p3p/set_cookie.php
site2 主页 /site2/p3p/index.php
---------------------------------------------------
图示:
① 从 www.site1.com 登陆
② 登陆后抓包:
③ 查看 mall.site1.com
④ 查看 www.site2.com
⑤ 从 www.site1.com 退出登录:
同时查看 mall.site1.com:
和 www.site2.com
==================================
附:
P3P 协议(Platform for Privacy Preference,隐私偏好设定平台),通过 p3p 协议也可以实现单点登录。
如果使用 P3P 协议实现 SSO 单点登录,可以在上面的代码中进行修改:首先设置 COOKIE 不再是各个站点的 set_cookie.php (例如:www.site1.com/p3p/set_cookie.php 或 www.site2.com/p3p/set_cookie.php),而是转移到 www.sso.com/set_cookie.php 中进行,经过验证之后设置所有相关站点的 COOKIE。由于是跨域设置 COOKIE,因此,在 www.sso.com/set_cookie.php 中应该加上:
header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');