Php开发完全跨站点跨域名单点(SSO)同步登录和注销
From:http://www.cnblogs.com/JinkoWu/p/5056646.html
先来说说什么是单点登录(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:

1 <?php
2 /**
3 * Created by PhpStorm.
4 * by Jinko Wu
5 * Date: 2015/12/17
6 */
7 error_reporting(E_ALL ^ E_NOTICE);
8 require "session.lib.php";
9 $session = jsession_start();
10
11 echo '<meta charset="utf-8"/>';
12 if(isset($session)) {
13 echo 'A 您好:' . $session['name'] . '<a href="http://www.a.cn/session-api.php?action=logout&sessid='.jsession_id().'">退出</a>';
14 } else {
15 echo 'A 您还没有登录!'.'<a href="http://www.a.cn/login.php">去登录</a>';
16 }

www.a.cn/login.php:

1 <?php
2 /**
3 * Created by PhpStorm.
4 * by Jinko Wu
5 * Date: 2015/12/17
6 */
7 ?>
8 <meta charset="utf-8"/>
9 <form action="session-api.php?action=login" method="post">
10 <input type="text" name="name">
11 <input type="hidden" name="redirect" value="<?php echo $_SERVER['HTTP_REFERER'] ?>">
12 <input type="submit">
13 </form>

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

1 <?php
2 /**
3 * Created by PhpStorm.
4 * by Jinko Wu
5 * Date: 2015/12/17
6 */
7 function jsession_start()
8 {
9 if(!$_COOKIE['__SESSID']) {
10 jsession_regenerate_id();
11 }
12
13 return jsession_update();
14 }
15
16 function jsession_update($session_id=null)
17 {
18 $session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id;
19
20 if($session_id == '') {
21 return null;
22 }
23
24 if($session_id && $data = jsession_is_valid($session_id)) {
25 jsession_save($data);
26 setcookie('__SESSID', $session_id, time()+jsession_live_time());
27 return $data;
28 }
29
30 return null;
31 }
32
33 function jsession_regenerate_id()
34 {
35 $sessid = jsession_generate_id();
36
37 if(jsession_id() != '' && file_exists('.sessioncache/' .jsession_id())) {
38 rename('.sessioncache/' .jsession_id(), '.sessioncache/' .$sessid);
39 }
40
41 $_COOKIE['__SESSID'] = $sessid;
42 setcookie('__SESSID', $sessid, time()+jsession_live_time());
43 }
44
45 function jsession_generate_id()
46 {
47 return 's'.base_convert(rand(0, 9999999999).rand(0, 9999999999).rand(0, 9999999999).rand(0, 9999999999), 10, 36);
48 }
49
50 function jsession_id()
51 {
52 return $_COOKIE['__SESSID'];
53 }
54
55 function jsession_live_time()
56 {
57 $gc_maxlifetime = ini_get('session.gc_maxlifetime');
58 $gc_maxlifetime = $gc_maxlifetime == '' ? 1440 : $gc_maxlifetime;
59 return $gc_maxlifetime;
60 }
61
62 function jsession_is_valid($session_id)
63 {
64 $session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id;
65
66 if($session_id == '') {
67 return false;
68 }
69
70 if(file_exists('.sessioncache/' .$session_id)) {
71 $data = unserialize(@file_get_contents('.sessioncache/' . $session_id));
72 return time() <= $data['time'] ? $data['data'] : false;
73 } else {
74 return false;
75 }
76 }
77
78 function jsession_data($session_id=null)
79 {
80 $session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id;
81
82 if($session_id == '') {
83 return null;
84 }
85
86 if($data = jsession_is_valid($session_id)) {
87 return $data;
88 }
89
90 return null;
91 }
92
93 function jsession_save($data)
94 {
95
96 if(!is_dir('.sessioncache')) {
97 mkdir('.sessioncache', 0777);
98 }
99
100 if($_COOKIE['__SESSID'] == '') {
101 return null;
102 }
103
104 $file = '.sessioncache/'.$_COOKIE['__SESSID'];
105 $fp = fopen($file , 'w');
106
107 if(flock($fp , LOCK_EX)) {
108 fwrite($fp, serialize(array('time' => time() + jsession_live_time(), 'data' => $data)));
109 flock($fp, LOCK_UN);
110 }
111
112 fclose($fp);
113 return $data;
114 }
115
116 function jsession_destory($session_id)
117 {
118 if($session_id == '') {
119 return ;
120 }
121
122 if($session_id == $_COOKIE['__SESSID']) {
123 setcookie('__SESSID', '');
124 $_COOKIE['__SESSID'] = null;
125 }
126
127 if(file_exists('.sessioncache/' .$session_id)) {
128 @unlink('.sessioncache/' .$session_id);
129 }
130 }

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

1 <?php
2 /**
3 * Created by PhpStorm.
4 * by Jinko Wu
5 * Date: 2015/12/17
6 */
7 error_reporting(E_ALL ^ E_NOTICE);
8 require 'session.lib.php';
9
10 if($_REQUEST['action'] == 'check') {
11 $session = jsession_is_valid($_REQUEST['id']);
12 echo json_encode(array('session' => $session));
13
14 } else if($_REQUEST['action'] == 'logout') {
15 if($_REQUEST['sessid'] !== null) {
16 jsession_destory($_REQUEST['sessid']);
17 }
18
19 echo '<meta charset="utf-8"/>';
20 echo '退出登录成功, 正在跳转...';
21 $_SERVER['HTTP_REFERER'] = $_SERVER['HTTP_REFERER'] == '' ? '/' : $_SERVER['HTTP_REFERER'];
22 echo '<script type="text/javascript">window.location.href = "' . $_SERVER['HTTP_REFERER'] . '";</script>';
23
24 } else if($_REQUEST['action'] == 'login') {
25 jsession_start();
26 $data = jsession_save(array('name' => trim($_REQUEST['name'])));
27 $redirect = $_REQUEST['redirect'] ? $_REQUEST['redirect'] : 'http://www.a.cn';
28 echo '<meta charset="UTF-8"><script type="text/javascript">window.location.href = "'.$redirect.'";</script>';
29
30 } else {
31 $session = jsession_start();
32
33 if($session && trim($_REQUEST['call']) != '' && jsession_id() != '') {
34 echo $_REQUEST['call'] . '('.json_encode(array('sessid' => jsession_id(), 'session' => $session)).')';
35 }
36 }

www.b.net/index.php:

1 <?php
2 /**
3 * Created by PhpStorm.
4 * by Jinko Wu
5 * Date: 2015/12/17
6 */
7 ?>
8 <meta charset="utf-8"/>
9 <script type="text/javascript">
10 function setCookie(name,value)
11 {
12 var Days = 30;
13 var exp = new Date();
14 exp.setTime(exp.getTime() + Days*24*60*60*1000);
15 document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
16 }
17
18 //jsonp 登录函数
19 function jsonp_do_login(data)
20 {
21 document.getElementById('name').innerHTML = 'B 您好:' + data.session.name + '<a href="http://www.a.cn/session-api.php?action=logout&sessid='+data.sessid+'">退出</a>';
22 console.log(data);
23 setCookie('__SESSID', data.sessid);
24 }
25 </script>
26 <?php
27 error_reporting(E_ALL ^ E_NOTICE);
28 session_start();
29 $session = check_session();
30 $sessid = $_COOKIE['__SESSID'];
31
32 if($session) {
33 echo 'B 您好:' . $session['name'] . '<a href="http://www.a.cn/session-api.php?action=logout&sessid='.$sessid.'">退出</a>';
34 } else {
35 echo '<span id="name">B 您还没有登录!<a href="http://www.a.cn/login.php">去登录</a></span>';
36 }
37
38 function check_session()
39 {
40 $sessid = $_COOKIE['__SESSID'];
41 $json = file_get_contents("http://www.a.cn/session-api.php?id=$sessid&action=check");
42 $json_data = json_decode($json, true);
43
44 if($json_data == null || empty($json_data['session'])) {
45 return false;
46 } else {
47 return $json_data['session'];
48 }
49 }
50
51 ?>
52
53 <?php if(!$session): ?>
54 <script type="text/javascript" src="http://www.a.cn/session-api.php?call=jsonp_do_login&<?php echo rand()?>"></script>
55 <?php endif; ?>

点击这里下载打包好的代码: http://files.cnblogs.com/files/JinkoWu/MultiSiteSingleLogin.zip
最后附上一张示例图片:

Php开发完全跨站点跨域名单点(SSO)同步登录和注销的更多相关文章
- 完全跨站点跨域名单点(SSO)同步登录和注销
先来说说什么是单点登录(SSO).来自百科的介绍:SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.它包括可以将这次主 ...
- 基于Swift语言开发微信、QQ和微博的SSO授权登录代码分析
前言 Swift 语言,怎么说呢,有一种先接受后排斥.又欢迎的感觉,纵观国外大牛开源框架或项目演示,Swift差点儿占领了多半,而国内尽管出现非常多相关技术介绍和教程,可是在真正项目开发中使用的占领非 ...
- 网站跨站点单点登录实现--cookie
至于什么是单点登录,举个例子,如果你登录了msn messenger,访问hotmail邮件就不用在此登录.一般单点登录都需要有一个独立的登录站点,一般具有独立的域名,专门的进行注册,登录,注销等操作 ...
- IIS下设置跨域访问问题--Access-Control-Allow-Origin 站点跨域请求的问题
背景: 最近 开发中遇到新需求,把公司的OA系统迁移一套到小程序上面去 有些功能的信息是在小程序 查看 但是文件是在pc端上传的 例如:领导在外出办公 使用小程序查看xxxx.pdf文件 这个时候就 ...
- 跨站点端口攻击 – XSPA(SSPA)
许多Web应用程序提供的功能将数据从其他Web服务器,由于种种原因.下载XML提要,从远程服务器,Web应用程序可以使用用户指定的URL,获取图像,此功能可能会被滥用,使制作的查询使用易受攻击的Web ...
- asp.net关于Cookie跨域(域名)的问题
Cookie是一个伟大的发明,它允许Web开发者保留他们的用户的登录状态.但是当你的站点有一个以上的域名时就会出现问题了.在Cookie规范上 说,一个cookie只能用于一个域名,不能够发给其它的域 ...
- ASP.NET Core中的OWASP Top 10 十大风险-跨站点脚本攻击 (XSS)
不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址 本博文翻译自: https://dotnetcoretutorials.com/201 ...
- IBM Rational AppScan:跨站点脚本攻击深入解析
IBM Rational AppScan:跨站点脚本攻击深入解析 了解黑客如何启动跨站点脚本攻击(cross-site scripting,XSS),该攻击危害(及不危害)什么,如何检测它们,以 ...
- vue 开发和生产的跨域问题
开发阶段 在开发环境与后端调试的时候难免会遇到跨域问题,在 vue 项目中常用的是 proxyTable,这个用起来很方便. 打开 config 文件夹下面的 index.js,找到 dev 开发模式 ...
随机推荐
- struct2(一)第一个struct程序
说明:本系列是针对struct2学习过程,主要的目的: 1. 探索针对一个新的开源框架的学习过程. 2. 学习struct2,学习官方对struct2介绍的方法. 3.别把英语忘了. 1. 为了更加清 ...
- Go语言的构建方法总结
趁着近期要换工作的空闲时间,看了一下Go语言,与C++相比,Go语言的确在不少地方轻便了不少,例如:增加了内置的字符串类型.多个返回值.支持协程.简单的构建方法等等.使得在生产效率方面有了不少的提高. ...
- Oracle11gRAC安装
安装Oracle RAC 一.硬件环境 ①用虚拟机搭建两台机器,操作系统都为: [root@node1 ~]# cat /etc/issue Red Hat Enterprise Linux Serv ...
- HTML5新增的一些属性和功能之六——拖拽事件
拖放事件的前提是分为源对象和目标对象,你鼠标拖着的是源对象,你要放置的位置是目标对象,区分这两个对象是因为HTML5的拖放事件对两者是不同的. 被拖动的源对象可以触发的事件: 1).ondragsta ...
- [RxJS] Reactive Programming - Using cached network data with RxJS -- withLatestFrom()
So now we want to replace one user when we click the 'x' button. To do that, we want: 1. Get the cac ...
- .NET基础拾遗(4)委托和事件1
一.委托初窥:一个拥有方法的对象 (1)本质:持有一个或多个方法的对象:委托和典型的对象不同,执行委托实际上是执行它所“持有”的方法. (2)如何使用委托? ①声明委托类型(delegate关键字) ...
- Linq to Entities不识别方法
db.UserValidates.Include(a => a.User).Where(uv => u.UserValidates.Contains(uv, c)).ToList(); 执 ...
- visual studio 一直显示正在准备解决方案
首先重启电脑,无法解决的情况下执行以下步骤: Kill Visual Studio Open Visual Studio without loading a solution Disable Ankh ...
- Asp.net mvc4 + HighCharts + 柱状图
前端代码: @{ Layout = null;} <!DOCTYPE html> <html><head> <meta name="viewport ...
- poj3264 线段树
Balanced Lineup Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 44121 Accepted: 20715 ...