认识单点登录

初识单点登录

最初接触到单点登录要追溯到3年多以前了,那时候看到的只是passport,当时要做全国所有社区的登录,然后就照着内部文档写了代码,然后就接入了(这里要提一句是百度与腾讯一旦形成产品的技术项目,文档都很不错)然后就没有然后了......

而知识的珍贵程度却是这样的:

知识珍贵度排名:
听过 < demo过 < 实际工作用过 < 实际工作中被坑过< 实际工作中被多次坑过或者深入研究总结过

所以,脱离工作去学习node可能收效不大,脱离项目去说前端优化可能不让人信服,不被ie坑也认识不到兼容,这个时候我们就要抓住工作中的点,一点点深入进去了,也许这一轮的瓶颈就自然而然的突破了呢,这是我最近的想法,也试着朝着相关的方向努力。

今天想要写passport事实上是工作中用到了这个点了,如果我就照着内部文档接入了似乎意义不大,所以我准备就着这个机会骚骚了解下原理,期望将他变成第一类知识沉淀。

文中内容皆是自我总结或者查询,如果有误请您提出,文中代码比较简陋,仅帮助理解,勿用作实际项目

什么是单点登录

单点登录即single sign on,用以解决同一公司不同子产品之间身份认证问题,也就是说,a.baidu.com与b.baidu.com两个站点之间只需要登录一次即可。

一般来说单点登录实现原理为,首次访问一个站点时会被引导至登录页,用户登录验证通过,浏览器会存储一个关键key(一般采用cookie),用户访问其他系统时会带着这个key,服务器系统发现具有key标志后,会对其进行验证,如果通过便不需要再次登录了。

这里的关键为二,第一是key,其二为系统统一的认证机制,这当中key会有一系列规则保证登录的安全性,而统一的认证机制是单点登录的前提,key是由他提供出去,每次用户由以这个给出的key来这里验证,判断其有效性,这里的统一的验证平台便是passport,他负责着制作通行令牌,并且对其进行验证。

这里举个例子来说(有误请您指出):

子系统A统一到passport服务器鉴权,并且在passport处获取cookie,并将token加入url,跳转回子系统A
跳回子系统A后,使用token再次去passport验证(这里直接走webservice服务验证,不再跳转),验证通过便在A系统服务器生成session,以后访问子系统A,在有效时间内不到passport验证
进入子系统B,跳到passport鉴权,发现passport cookie已经存在(token),便直接跳回子系统B,url带有token值,使用token去验证流程与A一致

实现原理

以腾讯百度等公司产品来说,他们一般会有一个统一的域,比如:

baidu.com => tieba.baidu.com、music.baidu.com、baike.baidu.com......

qq.com => feiji.qq.com、cd.qq.com、music.qq.com

这类共享一个主域,但是这类网站往往还会提供给第三方使用,比如腾讯会给tencent.com与jd.com使用,这个时候一些实现细节又不一样,但有一点是确定的:

单点登录要求共享一套账号体系,至少保存各个子系统的公共信息

用户登录态,我们存于session中,这个时候第一类站点的单点登录便比较简单,以百度为例,直接设置baike.baidu.com与tieba.baidu.com的session为cookie domain为baidu.com便可解决问题。

第二种情况比较普遍,便是music.qq.com与jd.com要实现单点登录,这个便完全跨域了,SSO的做法是将登录态保存至公用域,一般是passport.xx.com,比如百度为passport.baidu.com。

这个时候tieba.baidu.com或者music.baidu.com的授权检查与退出操作全部由SSO(passport.baidu.com)来进行

简单实现

单点登录的调用表现一般有两种:

① 跳转

② 弹出层回调

当用户在子系统未登录时,便会携带相关参数,比如tieba.baidu.com去到SSO(passport.baidu.com)进行登录

登录成功SSO会生成ticket key并附加给源网址跳转回去,这个时候SSO上已经有用户的登录状态了,但是各个子系统仍然没有登录态,所以根据这个ticket设置各个子系统独立的登录态是需要的。

当在SSO验证结束后,会回到子系统,子系统会根据得到的ticket(SSO为此次登录生成的用户基本信息加密串)获取用户的基本信息,从而在本站设置登录态。

这里使用代码做一个说明,有2年没有写服务器端代码了,最后选来选去使用了node实现,才发现自己对node也很不熟悉啊!!!

passport的实现

首先我们看看鉴权代码的实现

 1 var path = require(‘path‘);
 2 var express = require(‘express‘);
 3 var router = express.Router();
 4 //偷懒,将token数据写入文件
 5 var fs = require(‘fs‘);
 6
 7 /* GET home page. */
 8 router.get(‘/‘, function (req, res, next) {
 9   if (!req.query.from) {
10     res.render(‘index‘, {
11       title: ‘统一登录passport‘
12     });
13     return;
14   }
15   var from = req.query.from;
16   var token = null;
17   var cookieObj = {};
18   var token_path = path.resolve() + ‘/token_user.json‘;
19   req.headers.cookie && req.headers.cookie.split(‘;‘).forEach(function (Cookie) {
20     var parts = Cookie.split(‘=‘);
21     cookieObj[parts[0].trim()] = (parts[1] || ‘‘).trim();
22   });
23   token = cookieObj.token;
24   //如果url带有token,则表明已经在passport鉴权过
25   if (token) {
26     //存在token,则在内存中寻找之前与用户的映射关系
27     //异步的
28     fs.readFile(token_path, ‘utf8‘, function (err, data) {
29       if (err) throw err;
30       if (!data) data = ‘{}‘;
31       data = JSON.parse(data);
32       //如果存在标志,则验证通过
33       if (data[token]) {
34         res.redirect(‘http://‘ + from + ‘?token=‘ + token);
35         return;
36       }
37       //如果不存在便引导至登录页重新登录
38       res.redirect(‘/‘);
39     });
40   } else {
41     res.render(‘index‘, {
42       title: ‘统一登录passport‘
43     });
44   }
45 });
46
47 router.post(‘/‘, function (req, res, next) {
48   if (!req.query.from) return;
49   var name = req.body.name;
50   var pwd = req.body.password;
51   var from = req.query.from;
52   var token = new Date().getTime() + ‘_‘;
53   var cookieObj = {};
54   var token_path = path.resolve() + ‘/token_user.json‘;
55   //简单验证
56   if (name === pwd) {
57     req.flash(‘success‘, ‘登录成功‘);
58     //passport生成用户凭证,并且生成令牌入cookie返回给子系统
59     token = token + name;
60     res.setHeader("Set-Cookie", [‘token=‘ + token]);
61     //持久化,将token与用户的映射写入文件
62     fs.readFile(token_path, ‘utf8‘, function (err, data) {
63       if (err) throw err;
64       if (!data) data = ‘{}‘;
65       data = JSON.parse(data);
66       //以token为key
67       data[token] = name;
68       //存回去
69       fs.writeFile(token_path, JSON.stringify(data), function (err) {
70         if (err) throw err;
71       });
72     });
73     res.redirect(‘http://‘ + from + ‘?token=‘ + token);
74   } else {
75     console.log(‘登录失败‘);
76   }
77 });
78 module.exports = router;

子系统A的实现

 1 var express = require(‘express‘);
 2 var router = express.Router();
 3 var request = require(‘request‘);
 4
 5 /* GET home page. */
 6 router.get(‘/‘, function (req, res, next) {
 7   var token = req.query.token;
 8   var userid = null;
 9   //如果本站已经存在凭证,便不需要去passport鉴权
10   if (req.session.user) {
11     res.render(‘index‘, {
12       title: ‘子产品-A-‘ + req.session.user,
13       user: req.session.user
14     });
15     return;
16   }
17   //如果没有本站信息,又没有token,便去passport登录鉴权
18   if (!token) {
19     res.redirect(‘http://passport.com?from=a.com‘);
20     return;
21   }
22   //存在token的情况下,去passport主站检查该token对应用户是否存在,
23   //存在并返回对应userid
24   //这段代码是大坑!!!设置的代理request不起效,调了3小时
25   request(
26     ‘http://127.0.0.1:3000/check_token?token=‘ + token + ‘&t=‘ + new Date().getTime(),
27     function (error, response, data) {
28       if (!error && response.statusCode == 200) {
29         data = JSON.parse(data);
30         if (data.code == 0) {
31           //这里userid需要通过一种算法由passport获取,
32           //这里图方便直接操作token
33           //因为token很容易伪造,所以需要去主战验证token的有效性,
34           //一般通过webservers 这里验证就简单验证即可......
35           userid = data.userid;
36           //有问题就继续登录
37           if (!userid) {
38             res.redirect(‘http://passport.com?from=a.com‘);
39             return;
40           }
41           //取得userid后,系统便认为有权限去数据库根据用户id获取用户信息
42           //根据userid操作数据库流程省略......
43           // req.session.user = userid;
44           res.render(‘index‘, {
45             title: ‘子产品-A-‘ + userid,
46             user: userid
47           });
48           return;
49         } else {
50           //验证失败,跳转
51           res.redirect(‘http://passport.com?from=a.com‘);
52         }
53       } else {
54         res.redirect(‘http://passport.com?from=a.com‘);
55         return;
56       }
57     });
58 });
59 module.exports = router;

passport鉴权程序模拟

 1 var express = require(‘express‘);
 2 var router = express.Router();
 3 var path = require(‘path‘);
 4 var fs = require(‘fs‘);
 5
 6 /* GET users listing. */
 7 router.get(‘/‘, function(req, res, next) {
 8   var token = req.query.token;
 9   var ret = {};
10   var token_path = path.resolve() + ‘/token_user.json‘;
11   ret.code = 1;//登录失败
12   if(!token) {
13     res.json(ret);
14     return;
15   }
16   //如果传递的token与cookie不等也认为是鉴权失败
17   // if(token != cookieObj[‘token‘]){
18   //     res.json(ret);
19   //   return;
20   // }
21   fs.readFile(token_path, ‘utf8‘, function (err, data) {
22         if (err) throw err;
23         if(!data) data = ‘{}‘;
24         data = JSON.parse(data);
25         //如果存在标志,则验证通过,未考虑账号为0的情况
26         if(data[token]) {
27             ret.code = 0;//登录成功
28             ret.userid = data[token];
29         }
30         res.json(ret);
31     });
32 });
33 module.exports = router;

简单测试

测试之前需要设置host,这里继续采用fiddler神器帮助:

这边直接用node跑了3个服务器:

然后打开浏览器输入a.com,直接跳到了,登录页:

简单登录后(账号密码输入一致即可):

这个时候直接进入子系统B:

直接便登录了,说明方案大体上是可行的

结语

首先,这里的程序很简陋,很多问题,也没有做统一退出的处理,今天主要目的是了解单点登录,后面有实际工作再求深入。

这里附上源码,感兴趣的朋友看看吧:http://files.cnblogs.com/files/yexiaochai/web.rar,注意依赖包

时间: 2024-11-08 21:16:21

认识单点登录的相关文章

八幅漫画理解使用 JSON Web Token 设计单点登录系统

原文出处: John Wu 上次在<JSON Web Token – 在Web应用间安全地传递信息>中我提到了JSON Web Token可以用来设计单点登录系统.我尝试用八幅漫画先让大家理解如何设计正常的用户认证系统,然后再延伸到单点登录系统. 如果还没有阅读<JSON Web Token – 在Web应用间安全地传递信息>,我强烈建议你花十分钟阅读它,理解JWT的生成过程和原理. 用户认证八步走 所谓用户认证(Authentication),就是让用户登录,并且在接下来的一段时

单点登录原理理解

最近接触了一点单点登录的知识,有一点理解,记录一下.有些问题并没有找到完美的解决方法,还需要找点已有框架来看看. 欢迎留言探讨. 1       概念 1.1     概念及理解 有一个网上广为流传的定义“SSO是一种统一认证和授权机制,指访问同一服务器不同应用中的受保护资源的同一用户,只需要登录一次,即通过一个应用中的安全验证后,再访问其他应用中的受保护资源时,不再需要重新登录验证”. 个人理解,其中的“同一服务器”的限制应当去掉,关键在于“一次登录”,另外,很少有人提及“退出”的处理逻辑,因

单点登录(一)使用Cookie+File实现单点登录

本文使用Cookies+Filter实现www.taobao.tgb.com 和 www.tianmao.tgb.com的单点登录. 源码分享:链接: http://pan.baidu.com/s/1eQheDpS 密码: gn9d 一 实现原理 使用用户名和密码登录taobao后,会将用户名存储在Session和Cookie中各一份.当用户登录tianmao时,可直接从Cookie中获取用户名和密码,不需要二次登陆. 二 知识点解析 1.本例使用tomcat做服务器,绑定1个域名,且此域名对应

构建和实现单点登录解决方案

将一个开放源码的基于 Java 的身份验证组件集成进 Web 门户中 在现有的应用程序中实现单点登录解决方案(single sign-on,SSO,即登录一次,就可以向所有网络资源验证用户的身份)是非常困难的,但是在构建复杂的门户时,每个开发人员都要面对这个问题.因为门户需要与后端资源集成,而每个后端资源都有自己的身份验证需求,所以门户常常必须向用户提供单点登录特性.在本文中,Chris Dunne 一步步地描述了他为一个 Web 门户构建单点登录解决方案的经历.他将讲解如何设置一个开放源码解决

confluence jira crowd mysql等资源补充-单点登录等

http://atlassian.csdn.net/module/btc/atlassian/index csdn 代理文档等 confluence大陆代理官网,含中文演示 http://www.confluence.cn jira安装及破解操作步骤 有邮件配置 http://wenku.baidu.com/link?url=J-5DVCAgfhGUvIZwK0NebkaamTLj4aWA8bMmhDQDp2zbGltokdbp_SVQXqG7Kt52fygm_RqFr71H98b5y2maId

Atitit. 单点登录sso 的解决方案 总结

Atitit.  单点登录sso 的解决方案 总结 1. 系统应用场景and SSO模式选型 2 2. 系统应用的原则与要求 2 2.1. 开发快速简单::绝大部分系统来说,开发快速简单为主 2 2.2. 支持token交换,这样有利于集成先有的系统模块无需大改动,仅仅需要改动登陆模块.. 2 2.3. 支持用户名映射.当多个子系统username不同时候儿 2 3. 脱机验证sso (分散认证,类似护照的识别方式) 2 3.1. 适合场景:: 高性能场景,支持单方面改造 3 3.2. 缺点::

使用WIF实现单点登录Part I——Windows Identity Foundation介绍及环境搭建

首先先说一下什么是WIF(Windows Identity Foundation).由于各种历史原因,身份验证和标识的管理一般都比较无规律可循.在软件里加入"身份验证"功能意味着要在你的代码里混进处理底层任务(如验证用户名和密码,与X509证书或类似的证书打交道等)的代码.这样一来就得对基础架构相当依赖,程序很难移植,除非大范围重写.要改变这种情况,使用基于声明的标识(claims-based identity)可以很好的解决这个问题.这个"基于声明的标识"是神马东

使用WIF实现单点登录Part II —— Windows Identity Foundation基本原理

在上一篇文章中,我们已经使用WIF构建了一个基于MVC4的简单的身份验证程序,在这篇文章里,我们将探讨一下到底什么是WIF,以及它的工作原理.然后在下一篇文章开始,我们将实际操作,实现单点登录功能. 身份标识的挑战 在上一篇文章也提及到了,大部分的开发人员并不是安全方面的专家,很多人对于身份验证,授权以及用户体验个性化等工作感觉非常的不爽.传统的计算机技术的课程里通常也不会教这些课题,因此这些东西经常在软件开发周期的后半部分才会凸显出来.当今,一个公司里有数十上百个Web应用程序和服务已不是什么

【开发笔记】单点登录CAS测试登录Invalid credentials无效凭据

今天公司要搭建单点登录DEMO,完成第一步简单的搭建成功后测试成功进到登陆界面. 在网上的资料和搭建的流程文档上都写用户名和密码输入相同的就可以登录成功,然而测试后发现会报出如下错误: 经过查找很多资料发现可能是文档和网上的教程资料时间较久,新版本做了改变, 现在的用户名和密码分别为 casuser Mellon (注意密码M是大写) 测试之后进入成功!

CAS单点登录

1.      CAS 简介 1.1.  What is CAS ? CAS ( Central Authentication Service ) 是 Yale 大学发起的一个企业级的.开源的项目,旨在为 Web 应用系统提供一种可靠的单点登录解决方法(属于 Web SSO ). CAS 开始于 2001 年, 并在 2004 年 12 月正式成为 JA-SIG 的一个项目. 1.2.  主要特性 1.   开源的.多协议的 SSO 解决方案: Protocols : Custom Protoc