先来说说什么是单点登录(SSO)。来自百科的介绍:SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。

首先想到的单点登录, 应该就是, 在某个服务器或者站点进行登录, 登录之后携带登录ticket, 跳转到原来登录的站点。原来登录的站点通过远程验证ticket是否合法。这个方法很容易, 只要所有连接都带上ticket参数, 就可以不用登录了。

这样单点登录会有一个局限性, 比如下例场景(如天猫和淘宝):www.a.cn和www.b.net两个站点都在浏览器中打开了, 两个站点都未登录。然后a站点登录, 再切换到b站点的窗口刷新浏览器, 可以发现b站点依然没有登录。因为b站点没有ticket。

如何实现呢? 下面有我自己(Jinko)的个人想法:a站点登录后, 将ticket存到cookie中, 此时a站点是已经登录的。但是b站点无法读取a站点的cookie。因为b站点和a站点是不同根域下的, 这个时候我们应该想到跨域。想到跨域就会想到ajax, canvas多么蛋疼(canvas在将非当前域的img绘制到画布中时进行toDataURL时会有安全限制),他们都会因为跨域安全问题而限制了(当然还有iframe :))。 解决方案就是jsonp, 每当想到跨域我就想到jsonp。没错, 它确实为跨域而生(要不然也没人用它)。

好, 有了jsonp以上问题就迎刃而解, 思路变成如下:

a站点登录后, b站点通过jsonp获取a站点的ticket, 写到当前站点(b)的cookie里,并执行登录动作,更新页面数据。这时候, 后续的所有页面都可以通过cookie里的ticket进行远程验证。而且从a站点到b站点都无需再url中带上ticket(除了注销登录)。真是方便多了。a站点注销登录就直接从cookie取ticket并将对于ticket对应的数据删去(一般存于数据库,或者redis、memcache缓存里面)。在下面的例子里, 为方便我是存于文件 O(∩_∩)O。

接下来就是代码例子, 我用的是php来实现, 代码结构如下

index.php为站点首页, login.php为登录页面, session.lib.php 是自己实现的session存储机制, session-api.php是统一登录接口,.sessioncache目录存的是session缓存文件。看文件目录结构相当简单

要注意的是, 请不要直接用localhost去访问, 要新建两个虚拟主机分别用域名www.a.cn和www.b.net。请修改host文件使它们指向127.0.0.1

以下是代码:

www.a.cn/index.php:

<?php
/*** Created by PhpStorm.* by Jinko Wu* Date: 2015/12/17*/
//允许IE等浏览器跨域访问cookie
header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
error_reporting(E_ALL ^ E_NOTICE);
require "session.lib.php";
$session = jsession_start();echo '<meta charset="utf-8"/>';
if(isset($session)) {echo 'A 您好:' . $session['name'] . '<a href="http://www.a.cn/session-api.php?action=logout&sessid='.jsession_id().'">退出</a>';
} else {echo 'A 您还没有登录!'.'<a href="http://www.a.cn/login.php">去登录</a>';
}

www.a.cn/login.php:

<?php
/*** Created by PhpStorm.* by Jinko Wu* Date: 2015/12/17*/
//允许IE等浏览器跨域访问cookie
header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
?>
<meta charset="utf-8"/>
<form action="session-api.php?action=login" method="post"><input type="text" name="name"><input type="hidden" name="redirect" value="<?php echo $_SERVER['HTTP_REFERER'] ?>"><input type="submit">
</form>

www.a.cn/session.lib.php:

<?php
/*** Created by PhpStorm.* by Jinko Wu* Date: 2015/12/17*/
function jsession_start()
{if(!$_COOKIE['__SESSID']) {jsession_regenerate_id();}return jsession_update();
}function jsession_update($session_id=null)
{$session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id;if($session_id == '') {return null;}if($session_id && $data = jsession_is_valid($session_id)) {jsession_save($data);setcookie('__SESSID', $session_id, time()+jsession_live_time());return $data;}return null;
}function jsession_regenerate_id()
{$sessid = jsession_generate_id();if(jsession_id() != '' && file_exists('.sessioncache/' .jsession_id())) {rename('.sessioncache/' .jsession_id(), '.sessioncache/' .$sessid);}$_COOKIE['__SESSID'] = $sessid;setcookie('__SESSID', $sessid, time()+jsession_live_time());
}function jsession_generate_id()
{return 's'.base_convert(rand(0, 9999999999).rand(0, 9999999999).rand(0, 9999999999).rand(0, 9999999999), 10, 36);
}function jsession_id()
{return $_COOKIE['__SESSID'];
}function jsession_live_time()
{$gc_maxlifetime = ini_get('session.gc_maxlifetime');$gc_maxlifetime = $gc_maxlifetime == '' ? 1440 : $gc_maxlifetime;return $gc_maxlifetime;
}function jsession_is_valid($session_id)
{$session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id;if($session_id == '') {return false;}if(file_exists('.sessioncache/' .$session_id)) {$data = unserialize(@file_get_contents('.sessioncache/' . $session_id));return time() <= $data['time'] ? $data['data'] : false;} else {return false;}
}function jsession_data($session_id=null)
{$session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id;if($session_id == '') {return null;}if($data = jsession_is_valid($session_id)) {return $data;}return null;
}function jsession_save($data)
{if(!is_dir('.sessioncache')) {mkdir('.sessioncache', 0777);}if($_COOKIE['__SESSID'] == '') {return null;}$file = '.sessioncache/'.$_COOKIE['__SESSID'];$fp = fopen($file , 'w');if(flock($fp , LOCK_EX)) {fwrite($fp, serialize(array('time' => time() + jsession_live_time(), 'data' => $data)));flock($fp, LOCK_UN);}fclose($fp);return $data;
}function jsession_destory($session_id)
{if($session_id == '') {return ;}if($session_id == $_COOKIE['__SESSID']) {setcookie('__SESSID', '');$_COOKIE['__SESSID'] = null;}if(file_exists('.sessioncache/' .$session_id)) {@unlink('.sessioncache/' .$session_id);}
}

www.a.cn/session-api.php:

<?php
/*** Created by PhpStorm.* by Jinko Wu* Date: 2015/12/17*/
//允许IE等浏览器跨域访问cookie
header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
error_reporting(E_ALL ^ E_NOTICE);
require 'session.lib.php';if($_REQUEST['action'] == 'check') {$session = jsession_is_valid($_REQUEST['id']);echo json_encode(array('session' => $session));} else if($_REQUEST['action'] == 'logout') {if($_REQUEST['sessid'] !== null) {jsession_destory($_REQUEST['sessid']);}echo '<meta charset="utf-8"/>';echo '退出登录成功, 正在跳转...';$_SERVER['HTTP_REFERER'] = $_SERVER['HTTP_REFERER'] == '' ? '/' : $_SERVER['HTTP_REFERER'];echo '<script type="text/javascript">window.location.href = "' . $_SERVER['HTTP_REFERER'] . '";</script>';} else if($_REQUEST['action'] == 'login') {jsession_start();$data = jsession_save(array('name' => trim($_REQUEST['name'])));$redirect = $_REQUEST['redirect'] ? $_REQUEST['redirect'] : 'http://www.a.cn';echo '<meta charset="UTF-8"><script type="text/javascript">window.location.href = "'.$redirect.'";</script>';} else {$session = jsession_start();if($session && trim($_REQUEST['call']) != '' && jsession_id() != '') {echo $_REQUEST['call'] . '('.json_encode(array('sessid' => jsession_id(), 'session' => $session)).')';}
}

www.b.net/index.php:

<?php
/*** Created by PhpStorm.* by Jinko Wu* Date: 2015/12/17*/
?>
<meta charset="utf-8"/>
<script type="text/javascript">function setCookie(name,value){var Days = 30;var exp = new Date();exp.setTime(exp.getTime() + Days*24*60*60*1000);document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();}//jsonp 登录函数function jsonp_do_login(data){document.getElementById('name').innerHTML = 'B 您好:' + data.session.name + '<a href="http://www.a.cn/session-api.php?action=logout&sessid='+data.sessid+'">退出</a>';console.log(data);setCookie('__SESSID', data.sessid);}
</script>
<?php
error_reporting(E_ALL ^ E_NOTICE);
session_start();
$session = check_session();
$sessid = $_COOKIE['__SESSID'];if($session) {echo 'B 您好:' . $session['name'] . '<a href="http://www.a.cn/session-api.php?action=logout&sessid='.$sessid.'">退出</a>';
} else {echo '<span id="name">B 您还没有登录!<a href="http://www.a.cn/login.php">去登录</a></span>';
}function check_session()
{$sessid = $_COOKIE['__SESSID'];$json = file_get_contents("http://www.a.cn/session-api.php?id=$sessid&action=check");$json_data = json_decode($json, true);if($json_data == null || empty($json_data['session'])) {return false;} else {return $json_data['session'];}
}?><?php if(!$session): ?><script type="text/javascript" src="http://www.a.cn/session-api.php?call=jsonp_do_login&<?php echo rand()?>"></script>
<?php endif; ?>

点击这里下载打包好的代码: http://files.cnblogs.com/files/JinkoWu/MultiSiteSingleLogin.zip

最后附上一张示例图片:

完全跨站点跨域名单点(SSO)同步登录和注销相关推荐

  1. ASP.NET站点跨子域名单点登陆(SSO)的实现

    http://blog.csdn.net/jason_dct/article/details/8502075 ASP.NET站点跨子域名单点登陆(SSO)的实现 在MSDN的文档"配置跨应用 ...

  2. SSO单点登录跨域跨服务器

    单点登录系统总结 关于登录 一.登录 1.当用户点击登录的时候,把当前页面的url用参数传递到登录页面 2.用户成功登录,生成token,保存到redis中(service层),key为token,v ...

  3. 多域名同步登录,单点登录SSO

    [多个域名+1个登录域名.登录成功后将其他多个域名分配SESSION值,即实现了多域名同时登录的情况.登出同理!] session_start();//这里 会默认创建一个COOKIE名为:PHPSE ...

  4. Web应用跨域访问及单点登录解决方案汇总

    做过跨越多个网站的Ajax开发的朋友都知道,如果在A网站中,我们希望使用Ajax来获得B网站中的特定内容,如果A网站与B网站不在同一个域中,那么就出现了跨域访问问题.Ajax的跨域访问问题是现有的Aj ...

  5. 【burpsuite安全练兵场-客户端12】跨站点请求伪造CSRF-12个实验(全)

    前言: 介绍: 博主:网络安全领域狂热爱好者(承诺在CSDN永久无偿分享文章). 殊荣:CSDN网络安全领域优质创作者,2022年双十一业务安全保卫战-某厂第一名,某厂特邀数字业务安全研究员,edus ...

  6. cookie跨域,实现单点登录

      Cookie 跨域,实现单点登录 Table title                         最近在做一个单点登录的系统整合项目,之前我们使用控件实现单点登录(以后可以介绍一下).但现 ...

  7. php跨域同步登录,织梦PC端移动端会员同步登录跨域AJAX

    利用织梦分别做移动端和PC端的时候会涉及到跨域问题,也就是说移动端和PC端采用不同的域名,就是所谓的跨域. 要实现PC端和移动端会员同步登录,用默认的AJAX来实现会员同步登录的方法就不再适用了,因为 ...

  8. 记录如何防止跨站点脚本攻击之抄写

    一.简介 跨站点脚本(XSS)是当前web应用中最危险和最普遍的漏洞之一.安全研究人员在大部分最受欢迎的网站,包括Google, Facebook, Amazon, PayPal等网站都发现这个漏洞. ...

  9. 跨站点脚本(XSS)

    1. 简介 跨站点脚本(XSS)是当前web应用中最危险和最普遍的漏洞之一.安全研究人员在大部分最受欢迎的网站,包括Google, Facebook, Amazon, PayPal等网站都发现这个漏洞 ...

最新文章

  1. python3.5安装教程-Python 3.5安装教程
  2. 张槎地铁站定位综合枢纽 与多条重要轨道交汇
  3. C++的iostream标准库介绍
  4. 2020正收益女性占比高达 58.9%,数据揭秘男女谁更会理财?
  5. 华农计算机学院院长,华农大生命科学技术学院副院长到武生院任职
  6. 基于RNGCryptoServiceProvider的洗牌算法
  7. Spring IoC — 基于Java类的配置
  8. Vue.js-----轻量高效的MVVM框架(五、计算属性)
  9. mysqldump怎么用 mysqldump没反应 mysqldump语法错误 mysqldump备份 mysql恢复 source命令 采用Navicat备份与mysqldump备份的区别...
  10. eclipse svn插件安装_eclipse 2020-03 (4.15.0) SVN 插件在线安装教程
  11. 北京1954坐标转为经纬度坐标
  12. 基于RAM的雷达线性调频信号产生
  13. BackgroundWorker的参数传递
  14. 大数据最佳实践-spark
  15. C++“准”标准库Boost学习指南(3):Boost.Utility
  16. 100流明相当于多少w_lx和瓦数换算(1lx等于多少w)
  17. 图像mnf正变换_PIE SDK最小噪声变换
  18. 美化你的Xfce桌面
  19. selinux基本概念 | 开启selinux策略 | 安全上下文的临时修改 | 安全上下文的永久修改 | 如何修复selinux | selinux对服务功能的影响 | 系统自动排错
  20. 【Java实现】南京地铁导航系统的简单实现(一)—— 存储站点信息

热门文章

  1. 织梦php标签查询数据库,织梦DedeCMS模板标签sql调用代码大全
  2. 《暧昧的日本人》--李兆忠
  3. 亚马逊跟卖有效找listing,适合各阶段卖家
  4. 局域网访问虚拟机WSX服务器,VMware的WSX
  5. html select 样式t调整_select标签样式美化
  6. 【面试题】谈谈你对vite的了解
  7. API流程和代码结构
  8. 【基于文本相似度的论文查重系统-哔哩哔哩】 https://b23.tv/6JJBMur
  9. PHP文件锁同步实例
  10. Unity3D作业六项目一——优化打飞碟游戏