SegmentFault 单点登录最新的文章
2016-03-04T21:48:21+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
一、实现简单Web-SSO之总述
https://segmentfault.com/a/1190000004541984
2016-03-04T21:48:21+08:00
2016-03-04T21:48:21+08:00
mistkafka
https://segmentfault.com/u/mistkafka
3
<h3>目录</h3>
<p><a href="https://segmentfault.com/a/1190000004541984">一、总述</a><br><a href="https://segmentfault.com/a/1190000004532670?_ea=641739">二、环境配置</a><br>[三、业务servers管理]<br>[四、用户信息同步]<br>[五、token的拦截、生成、验证(服务端)]<br>[六、概念辨析:session、cookie、token的区别与token存储]<br>[七、token的分享(浏览器端)]<br>[八、安全性措施]<br>[九、用户体验]<br>[十、性能]</p>
<hr>
<h3>关于没有后续更新的问题</h3>
<p>各位,实在抱歉!SSO原本是我的毕业设计,但是出于某个原因,我决定留级,毕设暂时搁浅!这段时间忙着准备面试的事情,系统学习前端中ing! 这是这个系列被搁浅的<strong>原因之一</strong>!另一个原因在于,我发现没有经历过企业开发,真的在某些环节上很难理解SSO中一些让我非常困惑的问题!比如,我一直困惑的<a href="https://segmentfault.com/q/1010000004697715">这个</a>问题,这非常有可能是一个很简单的问题,但是我往往是被这种问题困惑住。比如,“性能”问题,大四实习年一直没在IT企业,讨论“性能”心里很虚,我更想接触一定的开发之后,心里更加有底的写下问题的答案。没出问题的话,这个系列会在6月中旬继续更新,希望8月能搞定。</p>
<hr>
<h3>经商之道</h3>
<p>今天我要讲两个故事,一家高级商场的故事和一家普通商场的故事。</p>
<h6>1.高级商场的故事</h6>
<p>从前有个老板叫<strong>C/S</strong>,他开了这样一家高级商场叫<strong>“TCP/IP Server"</strong>。专门服务注册了VIP的顾客。他们对待注册了VIP的顾客的服务非常的高级。怎样高级?每一位VIP顾客进来购物,就会有一个服务员全程陪同。这些服务员提供所有的服务,非常的有才能。但就是都很没有记性,永远都记不住自己是在服务哪位顾客。这也怪不得服务员,谁叫每个VIP顾客都长得一模一样呢?于是老板就让服务员全程牵着顾客的手,并且手里拿着一张纸条,纸条上面写着顾客的姓名、性别、年龄、余额等等信息。这样他们就能叫出顾客的尊称,而且因为牵着手,所以不至于跟顾客走丢。真为这一群奇葩着急啊ヽ( ̄д ̄;)ノ!!!这些服务员这一群体被学术家们称为“progress”,而顾客这一群体被学术家们称为“client”</p>
<p>我很好奇高级商场卖的都是些什么东西。从商业角度来说,全程一对一的服务成本可真高啊。</p>
<h5>2.普通商场的故事</h5>
<p>后来,有一个老板叫<strong>B/S</strong>,他觉得<strong>C/S</strong>是个傻X,全程一对一的服务成本高,收益小。于是他开了一家普通的商场叫"HTTP Server"。跟"TCP/IP Server"商场一样,他的"HTTP Server"也是需要注册VIP的。但是他的服务员们并不会全程陪同顾客购物。总的来说,他们提供两样服务:1.询问 2.收银。每个服务员在处理完某个顾客的<strong>询问服务</strong>或者<strong>收银服务</strong>后,就可以为另一个顾客服务。<strong>"B/S"</strong>以为它的商场会比那个没头脑的<strong>“C/S”</strong>的商场收益更好!很显然嘛,一样多的服务员,他的店能服务更多的顾客。</p>
<p>天真的<strong>B/S</strong>,可怜的<strong>B/S</strong>。他差点就破产了~ 还不是那群让人着急的服务员!开张第一天,他们就惹来了满满的差评。他们总是记不住顾客买了什么东西,根本没有办法收银;而且顾客上前要询问他们“node.js的产品在哪个货架?”、“Python有多少种框架产品?”等问题的时候,这群服务员总是没有办法先叫出顾客的名字来给予顾客问候。顾客觉得他们都很没有礼貌~~~这也怪不得服务员,健忘是他们的天性,更何况每个顾客的相貌、穿着、声音又都完全一样,他们可是要一个人服务多个顾客的!</p>
<p><strong>B/S</strong>决定关店整改!!!他给每个服务员配备了一个记录器。有一个服务员专门负责把进店的会员信息写进记录器,然后把一串唯一的ID写在一种叫做<strong>cookie</strong>的纸上,把纸贴在顾客额头上......没错,贴在额头上。其它服务员只要往记录器输入顾客头上的ID,就能查询到顾客的信息,这样服务员就不会因为叫不出顾客的名字被说没礼貌了;并且可以把顾客要买的东西,<strong>临时</strong>的记录在记录器里面。当然啦,记录器里的记录是整个商场共享的。所以,即便<strong>顾客A</strong>买《黑客与画家》的时候是<strong>服务员A</strong>给记录的,买《写给大家看的设计书》的时候是<strong>服务员B</strong>给记录的,结算的时候又是<strong>服务员C</strong>,他们搞错了。 <strong>B/S</strong>非常自豪的把这种记录器机制叫做<strong>session机制</strong>,真搞不懂他为什么要这么叫。就这样,<strong>B/S</strong>的<strong>“HTTP Server”</strong>商场以更低的成本获得了更大的收益~ 他发迹了。</p>
<h3>登录这件事</h3>
<p>登录就是通过验证(密码验证、指纹验证、声音验证、人面验证、DNA验证...),向服务端表明你是谁?并且让服务端记住你是谁。对于C/S的架构来说,要记住你是谁太容易了~ 客户端跟服务端建立的TCP/IP连接就能很好的表明你谁,一个进程服务一个客户端,只要在进程空间里写个变量就OK了!但是对于B/S架构来说,因为是无状态的,服务一结束就断开(现在的长连接也还是会断),要记住你是谁真心太不容易了~ 所以只能由客户端来告诉服务端:“我是谁。”,也就是给客户端一个ID,然后服务端有个全局的空间(session)用于记录ID对于的信息,类似于Map<ID, infomations>。对于<strong>每一次</strong>的服务请求,服务端都要拿着客户端给的ID,去session查询这个ID是不是登录了,如果是则怎样怎样,如果不是就叫用户登录(假设每个页面的访问都要求用户登录)。拿着ID去session查询ID是不是登录了这个过程我们把它叫做<strong>检验</strong>,这点先提前说明下,有点重要!登录成功后,就把这个ID做一个标记,表示登录。什么样的标记?你可以在这个ID的session空间里设置一个变量叫<code>mark</code>用来表示登录了,但是不会有人这么做的,至少也得设置个<code>username</code>的变量。不然你怎么知道这个用户是哪一个用户呢?有username就可以再去数据库查询更多的信息,当然也可以把更多的信息写到session里面,这里不做讨论。</p>
<h3>什么是单点登录(Single Sign-On)</h3>
<p>很早期的公司,一家公司可能只有一个Server,慢慢的Server开始变多了。每个Server都要进行注册登录,退出的时候又要一个个退出。用户体验很不好!你可以想象一下,上豆瓣 要登录豆瓣FM、豆瓣读书、豆瓣电影、豆瓣日记......真的会让人崩溃的。我们想要另一种登录体验:一家企业下的服务只要一次注册,登录的时候只要一次登录,退出的时候只要一次退出。怎么做?</p>
<p><strong>一次注册</strong>。 一次注册不难,想一下是不是只要Server之间同步用户信息就行了?可以,但这样描述不太完整,后续讲用户注册的时候详细说。实际上用户信息的管理才是SSO真正的难点,只是作为初学者,我们的难点在于实现SSO的技术!我们先讨论实现手段。</p>
<p><strong>一次登录与一次退出</strong>。 回头看看<strong>普通商场的故事</strong>,什么东西才是保持登录状态关键的东西?记录器(session)?那种叫做cookie的纸张?写在纸张上的ID? 是session里面<strong>记录的信息</strong>跟那个ID,cookie只不是记录ID的工具而已。客户端持有ID,服务端持有session,两者一起用来保持<strong>登录状态</strong>。客户端需要用ID来作为凭证,而服务端需要用session来验证ID的有效性(ID可能过期、可能根本就是伪造的找不到对于的信息、ID下对应的客户端还没有进行登录验证等)。但是session这东西一开始是每个server自己独有的,豆瓣FM有自己的session、豆瓣读书有自己的session,而记录ID的cookie又是不能跨域的。所以,我们要实现一次登录一次退出,只需要想办法让各个server的共用一个session的信息,让客户端在各个域名下都能持有这个ID就好了。再进一步讲,只要各个server拿到同一个ID,都能有办法检验出ID的有效性、并且能得到ID对应的用户信息就行了,也就是能<strong>检验ID</strong>。</p>
<h3>单点登录的实现方法(server端)</h3>
<p>以server群如何生成、验证ID的方式大致分为两种:</p>
<ul>
<li><p>“共享Cookie”<br>这个就是上面提到的共享session的方式,我倒觉得叫“共享session”来得好一点,本质上cookie只是存储session-id的介质,session-id也可以放在每一次请求的url里。据说这种方式不安全,我没去细究,哪位大神可以推荐下相关的资料,我后期补上。其实也是,毕竟session这项机制一开始就是一个server一个session的,把session拿出来让所有server共享确实有点奇怪。</p></li>
<li><p>SSO-Token方式<br>因为共享session的方式不安全,所以我们不再以session-id作为身份的标识。我们另外生成一种标识,把它取名SSO-Token(或Ticket),这种标识是整个server群唯一的,并且所有server群都能验证这个token,同时能拿到token背后代表的用户的信息。我们要讨论的也是这种方式,一会上具体流程图。</p></li>
</ul>
<h3>单点登录的实现方法(浏览器端)</h3>
<p>单点登录还有非常关键的一步,这一步跟server端验证token的方式无关,用最早的“共享session”的方式还是现在的“token”方式,身份标识到了浏览器端都要面临这样的一个问题:用户登录成功拿到token(或者是session-id)后怎么让浏览器存储和分享到其它域名下?同域名很简单,把token存在cookie里,把cookie的路径设置成顶级域名下,这样所有子域都能读取cookie中的token。这就是共享cookie的方式(这才叫共享Cookie嘛,上面那个应该叫共享session)。比如:谷歌公司,<code>google.com</code>是他的顶级域名,邮箱服务的<code>mail.google.com</code>和地图服务的<code>map.google.com</code>都是它的子域。但是,跨域的时候怎么办?谷歌公司还有一个域名,<code>youtube.com</code>,提供视频服务。如何把<strong>身份标识</strong>分享给youtube.com这个域?简单的先提下,后续会细说。</p>
<ul>
<li>
<p>利用带有src属性的HTML标签跨域设置cookie。例如:</p>
<pre><code><iframe width = 0 height=0 src="youtube.com/set_sso_token?ssotoken=iurk2i3f">
</code></pre>
</li>
<li><p>url跳转。比如:在google.com登录后,会跳转到youtube.com下种植cookie,再跳转会google.com。</p></li>
<li><p>Ajax设置cookie(目前还不了解,据说京东是这么做的。)</p></li>
</ul>
<h3>SSO-Token方式的服务端流程</h3>
<p>实际上,基本是用Token代替session的方式,也有非常多种实现方法。图片是我先去自己实验时的方案。</p>
<p><img src="/img/bVtdHy" alt="图片描述" title="图片描述"></p>
<p>SSO-Server负责用户的登录(当然也有注册、修改基本用户信息、退出功能),它的作用有两点:</p>
<ul>
<li><p>验证用户。就是验证账号密码。我们略过不关注</p></li>
<li><p>生成唯一的token</p></li>
<li><p>往其它业务Server同步token等信息,让各个业务server自己验证token有效性(如图); 或者,提供检验token有效性的API接口</p></li>
</ul>
<h3>我们要实现什么样的SSO</h3>
<ul>
<li><p>token生成与验证方案:如上图。</p></li>
<li><p>token在浏览器的跨域分享方法,都尝试一遍。</p></li>
<li><p>安全性。暂时一律不考虑,我们先关注实现一个差劲的SSO,再来关注安全。因为安全是个大话题。太早引入,太拖节奏</p></li>
<li><p>实现语言:node.js(express4.x)。其实感觉SSO的实现是语言无关的,一个node.js实现的登录server完全可以为一个python实现的业务server服务。server之间的关系最后都演化成web接口的调用。</p></li>
</ul>
二、 实现简单Web-SSO之环境配置
https://segmentfault.com/a/1190000004532670
2016-03-03T17:04:18+08:00
2016-03-03T17:04:18+08:00
mistkafka
https://segmentfault.com/u/mistkafka
0
<h3>目录</h3>
<p><a href="https://segmentfault.com/a/1190000004541984">一、总述</a><br><a href="https://segmentfault.com/a/1190000004532670?_ea=641739">二、环境配置</a><br>[三、业务servers管理]<br>[四、用户信息同步]<br>[五、token的拦截、生成、验证(服务端)]<br>[六、概念辨析:session、cookie、token的区别与token存储]<br>[七、token的分享(浏览器端)]<br>[八、安全性措施]<br>[九、用户体验]<br>[十、性能]</p>
<h3>学习前提</h3>
<ul>
<li><p>你已经知道什么是SSO了</p></li>
<li><p>了解域名解析流程与hosts文件的作用</p></li>
<li><p>知道nginx代理的使用</p></li>
<li><p>会启动一个web应用(python、java-web、php、node.js等等)</p></li>
</ul>
<p>本系列着重于SSO的实现,涉及到的技术主要以推荐阅读呈现。如果你不懂前提提到的点,可粗略浏览本文后去学习下技术细节再回头看,千万不要似动非懂的。另外声明,本实战是用node.js + express4.x实现的。</p>
<h3>hosts文件设置</h3>
<p>我们约定如下几个域名:</p>
<ul>
<li><p>id.vhost.com sso系统 负责验证登录、注册、修改公共用户信息</p></li>
<li><p>music.vhost.com 音乐应用 (同域)</p></li>
<li><p>note.com 日记应用(跨域)</p></li>
</ul>
<p>添加如下内容:</p>
<pre><code>127.0.0.1 id.vhost.com
127.0.0.1 music.vhost.com
127.0.0.1 note.com</code></pre>
<h3>nginx代理设置</h3>
<p>我们约定这三个server在127.0.0.1上的端口</p>
<ul>
<li><p>id.vhost.com --> 127.0.0.1:3000</p></li>
<li><p>music.vhost.com --> 127.0.0.1:3001</p></li>
<li><p>note.com --> 127.0.0.1:3002</p></li>
</ul>
<p>这里放一个参考配置代码,文件id.vhost.com.conf:</p>
<pre><code> 1 server {
2 listen 80;
3 server_name id.vhost.com;
4 charset utf-8;
5 access_log /var/log/nginx/id.vhost.com.log main;
6 location / {
7 proxy_set_header X-Real-IP $remote_addr;
8 proxy_set_header X-Forwarded-For $proxy_add_x_forwarde d_for;
9 proxy_set_header Host $http_host;
10 proxy_set_header X-NginX-Proxy true;
11
12 proxy_pass http://127.0.0.1:3000;
13 }
14 }</code></pre>
<p>其余两个自行配置</p>
<h3>项目初始化</h3>
<p>使用<a href="https://link.segmentfault.com/?enc=NnQq2ReQvo8ANVtQlTYR9Q%3D%3D.x33jVoTGZ%2FQ9hq1CfisgYn4vwll5vvKplREqCAzrmR030Yd1HRqM%2FcBiY21BayII" rel="nofollow">Express Generator</a>来生成项目</p>
<pre><code>express -e --git id // 我们使用ejs前端模板,同时添加.gitignore文件
express -e --git music
express -e --git note</code></pre>
<p>这样项目就生成完毕了!接下来修改启动端口。修改每个项目的bin/www文件中的端口设置。分别为3000、3001、3002(上面已经说了)</p>
<pre><code>var port = normalizePort(process.env.PORT || '3000');</code></pre>
<p>然后分别启动三个项目、以及nginx。在浏览器中访问id.vhost.com、music.vhost.com、note.vhost.com。 如果都成功,配置就完成了!这样,我们就在本地模拟出了跨域。</p>
<h3>反馈</h3>
<ul>
<li><p>请支出错误之处、不足需要补充之处好让我进行修改!</p></li>
<li><p>大胆说出你的疑问!</p></li>
</ul>
<h3>推荐阅读</h3>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=YgA83wvgi7uvZm1XK0TSXA%3D%3D.C49YJsjowhaybiN2DmcN05pwtyTSJMMZ4l3b9oQhNH8OKSwp%2FJPfvC3p1B36ITOeeAak8Iianb5i%2BKTydrbf0A%3D%3D" rel="nofollow">百度百科: SSO</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=Cb2BkEI4KxnDA0IJmevGSA%3D%3D.N76tRYad4%2B1rRLOKELgKbkaRnlpToAtlzbf7DV6F8CP%2BhTVfvA3Q1enUjI5t2gPG27ehlhGsA%2F1j4iNXvPK9%2Fw%3D%3D" rel="nofollow">维基百科: host文件</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=d5HPuFHtcLEVwVQo7435QA%3D%3D.%2FzGD%2BAvEk4Enc98TAgqJLuuYGk9CMvA4pTRldR5kcBFmR3s3DhV4d8y%2BGgEpeES7Oq0xYklF7MuwfBUuo9Ce1A%3D%3D" rel="nofollow">借助Nginx搭建反向代理服务器</a></p></li>
</ul>