来点高逼格的,使用前端Sendmessage实现SSO
关于什么叫SSO(单点登录),这个概念具体的信息我就不详述了,网上一搜一大把,总的来说,它是一种可以控制各个独立的系统经过它的授权后,
可以用同一个帐号体系登录不同的系统,达到一处登录,多处使用的效果。
最近的项目中就需要用到这个SSO,对我来说,我了解和接触的最早的SSO系统应该是康盛的Ucenter和PHPCMS的phpsso。还记得当时第一次看到它能在不同系统间实现同步
登录、同步退出这功能时,感觉好牛呀。技术人的思维就是想扒开看看,到底怎么做的?无奈当时技术能力有限,由于没怎么看懂,就没深入研究了,只要会用就行了。
当然了,现在再看上去已经没什么神秘的了,但在当时还是很震撼我这个小白的。
SSO其实明白了原理,不管如何变化,它的基本核心是不会变了。我们要实现的就是如何保持登录状态,如何判断你在各个系统有无登录?这个如果在单体应用中,那是再简单不过了
可以存在Cookie中,存在Session中等,那么SSO中应该如何去实现呢?其实,总的思路也是一样的,通过COOKIE在单独的服务器保留登录凭证,各系统之间独立去控制自已系统的状态。
看过淘宝,京东,百度,新浪等实现的SSO,一来看上去有点麻烦,需要在网址间跳来跳去,再者,它好像不太好满足我的项目。这些系统比如登录页一般都是有一个统一的登录页,但是
我这里要求每个登录页地址和风格可以不一样,甚至登录页有弹框和页面两种。这种情况下,其实用这种方式就不太好弄了。
于是,我和同事在考虑了一些方案,然后做了一些小demo后,决定使用前端实现SSO,就是说通过API接口的方式来实现。这种方案有个好处就是前后端是分离的,后端只需要实现
SSO的功能,前端去实现什么时候调用接口就行,没有页面跳转。就能达到不管你登录页怎么变,凡正后端接口和数据一样,至于展示就是前端的事了。
好了,方案定下来了,现在说说怎么实现吧??我这里可能会侧重于服务端一些,因为前端我不太熟悉,前端是由另一个同事实现的。
首先我们在网站A上嵌入一段隐藏的Iframe.
<div style="display:none>
<iframe src="https://dev.sso.com/auth" id="sso" width="0" height="0" frameborder="0" scrolling="0"></iframe>
</div>
dev.sso.com/auth这个页里面是一些与网站和后端通讯的代码。前端主要使用sendmessage在框架内外通讯。这个域名也是SSO单独认证的域名,就是说各个需要接入SSO功能的系统
都是通过这个域名来验证的。
通讯流程如下:
**1)打开网站A,前端通过sendmessage通讯,查看网站A有没有在dev.sso.com这个SSO服务器存在登录凭证(cookie存储)** **2)如果不存在,直接出登录页面,** **如果存在,则继续调用dev.sso.com/validate接口,这个接口会验证登录凭证的有效性,有效则表示登录了,同 时返回一个访问token(如果请求接口时,带上了token则验证合法后,** **返回这个token,如果token不合法,提示要重新登录,并清除单点上的登录凭证。如要没有带上,则生成一个新的),这个token用于后续网站其它接口的访问; 无效渲染出登录页。** **3)前端将返回的token存在本地,后续的接口都带上这个token就行了**4) 打开网站B,前端通过sendmessage通讯,发现在SSO服务器上有登录凭证。于是调用dev.sso.com/validate验证后,和第2步一样,返回访问token。服务端需要将这个访问token和用户
token作关联,表示是同一个用户。
5) 网站B通过这个访问token就能访问后续接口了。
至此,整个通讯就完成了,我们发现通过这样的方式,还是蛮简单的,也确实省去了很多步骤。而且一些安全性什么的在接口也可以一并处理了。没有暴露太多信息给前端。只有一个token
当然了,目前这种方案因为前端没有保存登录状态,每次都要实时去验证,这样对服务器是有一些性能消耗的。如果用户太多,那就问题更大了。而且,服务端缓存中存的绑定信息,如果用户太多
估计也会有不小的问题。
目前也只是实现了这样的SSO功能,其实SSO真要做起来也是一个很复杂的东西,除了登录,还有鉴权等,我们用户基数目前还没有这么大。这些问题暂时没有提到议程上来。这个方案也只是提供了一些思路,
SSO其实没有什么标准,完成可以通过自已项目中需要什么样的去开发就好。能保持状态,能判断登录就行。也不用限定语言,我们项目中使用的是Golang.
我这里贴一部分,服务端用到的部分缓存结构
//sso存的cookie缓存
1 sso_cookie:111111 {
"user_token":"123456"
"type":"USER.SSO.COOKIE"
}
//访问token与用户token对应关系
2 Login_User_token:888888 {
user_token:123456
service:"a.com"
}
Login_User_token:999999 {
user_token:123456
service:"b.com"
"type":"USER.ACCESS_TOKEN"
}
//用户token
3 Token:123456 {
"userid":"111111"
"username”:”test"
"type":"USER.REGULAR"
}
如上,用户在网站A,B上的访问token(888888,999999)都对应到用户token(123456)这个用户上面