先来说说什么是单点登录(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. Php开发完全跨站点跨域名单点(SSO)同步登录和注销

    From:http://www.cnblogs.com/JinkoWu/p/5056646.html 先来说说什么是单点登录(SSO).来自百科的介绍:SSO英文全称Single Sign On,单点 ...

  2. 网站跨站点单点登录实现--cookie

    至于什么是单点登录,举个例子,如果你登录了msn messenger,访问hotmail邮件就不用在此登录.一般单点登录都需要有一个独立的登录站点,一般具有独立的域名,专门的进行注册,登录,注销等操作 ...

  3. Yii2 多域名跨域同步登录退出

    在平台开发过程中,项目分为前台(frontend)www.xxx.com和后台(backend) yun.xxx.com两部分,绑定两个域名, 我们知道在没有绑定域名的时候前后台可以同步登录和退出,但 ...

  4. asp.net关于Cookie跨域(域名)的问题

    Cookie是一个伟大的发明,它允许Web开发者保留他们的用户的登录状态.但是当你的站点有一个以上的域名时就会出现问题了.在Cookie规范上 说,一个cookie只能用于一个域名,不能够发给其它的域 ...

  5. .Net 站点跨域问题及解决方法

    一.什么是站点跨域 了解跨域之前, 先了解下什么同源策略?百度百科:同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功 ...

  6. Appscan漏洞之跨站点请求伪造(CSRF)

    公司前段时间使用了Fortify扫描项目代码,在修复完这些Fortify漏洞后,最近又启用了Appscan对项目代码进行漏洞扫描,同样也是安排了本人对这些漏洞进行修复.现在,针对修复过的Appscan ...

  7. IIS下设置跨域访问问题--Access-Control-Allow-Origin 站点跨域请求的问题

    背景: 最近 开发中遇到新需求,把公司的OA系统迁移一套到小程序上面去 有些功能的信息是在小程序 查看 但是文件是在pc端上传的 例如:领导在外出办公 使用小程序查看xxxx.pdf文件  这个时候就 ...

  8. 跨站点脚本攻击XSS

    来源:http://www.freebuf.com/articles/web/15188.html 跨站点脚本攻击是一种Web应用程序的攻击,攻击者尝试注入恶意脚本代码到受信任的网站上执行恶意操作.在 ...

  9. 跨站点请求伪造 - SpringBoot配置CSRF过滤器

    1. 跨站点请求伪造   风险:可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务.   原因:应用程序使用的认证方法不充分. ...

随机推荐

  1. 【转】【Android】HAL分析

    原文网址:http://www.cnblogs.com/lcw/p/3335505.html HAL概述 以下是基于android4.0.3,对应其他低版本的代码,可能有所差异,但基本大同小异. An ...

  2. 【转】(总结)Linux下su与su -命令的本质区别

    原文网址:http://www.ha97.com/4001.html 本人以前一直习惯直接使用root,很少使用su,前几天才发现su与su -命令是有着本质区别的! 大部分Linux发行版的默认账户 ...

  3. libeXosip2(2-1) -- eXosip2 configuration API

    eXosip2 configuration API General purpose API. Data Structures struct   eXosip_dns_cache struct   eX ...

  4. 判断一个int 型整数 是否为回文数

    leetcode 上的题目 Determine whether an integer is a palindrome. Do this without extra space. 由于不能使用额外空间, ...

  5. NPOI之使用EXCEL模板创建报表

    因为项目中要用到服务器端创建EXCEL模板 无法直接调用EXCEL 查了下发现NPOI很方便很简单就实现了 其中走了点弯路 第一次弄的时候发现输出的值是文本不是数字型无法直接计算公式 然后又发现打开报 ...

  6. EffectiveC#2--为你的常量选择readonly而不是const

    1.对于常量,C#里有两个不同的版本: 编译时常量--效率相比更高些,但可维护性不好,保留的目的是为了性能.const关键字申明 public const int _Millennium = 2000 ...

  7. DataList、Repeater、GridView中的Checkbox取值问题

    先看页面代码 <asp:DataList id="DataList1" runat="server" Width="100%" Rep ...

  8. Could not load file or assembly 试图加载格式不正确的程序

    问题: 今天发布项目的时候遇到这个破问题,纳闷了好久,最后想起来自己改过程序生成的目标平台(原生成目标平台是Any CPU,被我改成了X86的). 解决方法: 改回原来的Any CPU 从新发布即可.

  9. 数据结构c++语言描述——最大堆(MaxHeap)

    一.最大堆的插入 图9-3a 给出了一个具有5个元素的最大堆.由于堆是完全二叉树,当加入一个元素形成6元素堆时,其结构必如9-3b 所示.如果插入元素的值为1,则插入后该元素成为2的左孩子,相反,若新 ...

  10. PowerDesigner中在生成的数据库脚本中用name列替换comment列作为字段描述的方法

    1 PowerDesigner中在生成的数据库脚本中用name列替换comment列作为字段描述的方法如下, 依次打开Tools -- Execute Commands -- Run Script,运 ...