一个简单的SSO统一登录设计

博观而约取,厚积而薄发。这篇文章主要讲述一个简单的SSO统一登录设计相关的知识,希望能为你提供帮助。
前言
随着公司的发展,公司内部各种系统后台也越来越多,需要进行的登录验证的操作也越来越多。不同的账号体系,不受限制的口令管理也成了一个问题。为了解决这些问题,需要对公司的登录管理进行统一,使用同一套的账号口令体系,并对口令的安全管控进行限制。于是单点登录的需求就变得很现实。单点登录-Single Sign On,简称SSO。可以理解为:一处登录,处处登录
以我司为例:内部系统的域名为 wwdz.com,设计单点登录的目标是,只要登录了其中的一个系统   a.wwdz.com 再打开其他接入单点登录的系统,比如 b.wwdz.com,可以直接获取用户信息,无需再次输入账号口令



SSO流程
具体步骤:假定都是第一次访问:1 用户通过浏览器访问 http://A.wwdz.com/some/page
2 浏览器向系统A发送请求
3 这是第一次访问系统A,系统A没有检测到用户的登录状态(cookie session等找不到)。认为其没有登录。此时A系统发起一个浏览器重定向,让浏览器跳转到统一登录并将redirectUrl参数设定为用户访问A系统时url,形如:??http://sso.wwdz.com/login?redirectUrl=http://A.wwdz.com/some/page???
4 浏览器接收到A系统的302跳转,访问 http://sso.wwdz.com/login?redirectUrl=http://A.wwdz.com/some/page
5 这是第一次访问SSO,同样的SSO没有检测到用户的登录状态,此时依照浏览器请求,返回SSO的login页面(页面是一个简单的表单,用于提交用户账号与口令)。
6 用户根据页面提示输入账号口令,并提交。
7 SSO接收数据进行校验:        用户账号口令校验不通过,不允许登录        用户账号口令校验通过,则:

  • 为其在SSO服务端创建session
  • 签发token(JWT格式),并在payload中指定过期时间等信息。
  • 通知浏览器进行重定向,目标是系统A,并把token作为url参数进行传递 或者 将token写入 cookie中(*.wwdz.com Authorization)
8 浏览器获得SSO的302返回,此时的请求中包含token 或者 在cookie中有token的值。
9 浏览器携带token访问A系统。
10 A系统根据请求信息获取token。
11 A系统将token写入Header Authorization中 请求SSO的attach/vaild接口已校验该token是否正确。
12 SSO接收A系统请求,校验token。token可能会出现的问题有:
token过期
用户在使用过程中,口令变化
token无法解码
用户被锁定

如果token有问题,则返回相应错误状态码如果token不符合上述状态,则将用户信息返回给系A统
13 系统A根据SSO返回的信息对用户最初的请求进行处理
14 浏览器对A系统返回页面进行展示。以上在用户第一次登录的情况下的一个流程。
如果用户已经登录了系统A,需要访问系统B,唯一不同的在第5步。按照上文逻辑,登录A系统必然会登录SSO,所以在登录系统B的过程,1-4步骤中的流程不变(只是将请求的Url换成http://B.wwdz.com/some/page),只是在第5步的时候,不再需要SSO返回用户登录页面而是直接根据用户信息生成token并拼装URL重定向给浏览器,就是第8步。其余流程不变。
以上就实现了一个SSO的登录流程。





钉钉扫码登录但是随着各种口令安全规章制度的完善,我司对用户口令的生命周期与强度都做了一定的要求,这就导致了一个很尴尬的问题:有些同学在设计了一个超复杂的口令后……把自己的口令忘记了。虽然提供重置口令功能,但不能总是在需要用口令的时候重置口令(虽然这个方法会提高口令更新频率,对安全来说是个好办法,但是还是影响工作效率),于是在现有SSO的基础上添加钉钉扫码登录功能
钉钉扫码登录的逻辑不是很复杂,钉钉提供了很详细的文档对此操作进行了描述。
在钉钉开放平台创建 移动接入应用 并在 登录 中创建 扫码登录应用授权 ,指定相应的回调地址、appId、 appSecret等信息。
在登录页引入钉钉的js文件,并设定回调地址等信息。
用户扫码并在手机上点击确认后,js会将获取的loginTmpCode返回给设定的回调地址
回调服务器获取loginTmpCode后构造相应的url跳转到钉钉服务器
如果钉钉服务器调用成功,则会302跳转到goto参数指定的redirect_uri,并向url参数中追加临时授权码code及state参数。
调用钉钉接口获取授权用户的个人信息。
将钉钉的个人信息与SSO中的信息进行关联实现免口令登录。
一键登录但是,随着时间的推移,又有同学说扫码登录还是要掏手机,不方便,于是乎萌生了一键登录的想法。

大家在使用QQ的时候可能会注意到:当用户使用QQ客户端之后,在使用浏览器访问腾讯的一些登录页面时,登录页面会获取到QQ信息,登录页面会变成QQ一键登录。实现这样的一键登录就要自己做一个客户端,客户端listen一个本地端口。浏览器在访问统一登录的时候请求这个本地端口以获取用户信息,进而实现一键登录的功能。
但是,这个想法的出现不仅仅是想做一键登录,也是由于VPN客户端安装使用的不方便(这是另一个话题了)。
为了实现这个功能,首先要改造一下SSO登录页面。需要假定一个请求用户信息的本机接口。
默认情况下一键登录是不显示的。但如果这个接口是通的,则隐藏原有的登录界面,直接提示一键登录,并获取从该接口获取相应的用户鉴权信息(username passwd)。以下是添加到SSO登录页中的js代码。
$(document).ready(function()
document.getElementById(autologin).style.display=none
$.get("http://127.0.0.1:xxxx",function(data,status)
let datas = data.split( )
let username = datas[0]
let passwd = datas[1]
console.log("username = " + username)
console.log("passwd = " + passwd)
document.getElementById(login).style.display=none
document.getElementById(autologin).style.display=
document.getElementById(autousername).value=https://www.songbingjia.com/android/username
document.getElementById(autopasswd).value=https://www.songbingjia.com/android/passwd
);
);

之后就是创建客户端,具体逻辑可以再写一篇,这里直说一下实现方案。
由于公司里使用MAC和win的都很多,所以搞一套易于扩展的跨平台方案是很必要的。最后我选择了Electron。
在我的理解中,Electron可以看做是用有一定限制的浏览器做GUI的跨平台APP解决方案。
启动了这个客户端app后,它会监听本机的指定端口,一旦被访问就会返回相应账号信息(只有用户登录并开启一键登录之后才会监听),这样就能与改造后的SSO登录页面进行适配,效果如下
客户端效果(由于是内嵌浏览器,基本的界面都是与SSO登录页保持一个风格,vpn客户端功能以后再说)

SSO统一登录在检测到一键登录后的效果
点击一键登录后,页面会自动将鉴权信息发送给SSO,其流程完全没有改变。
The END

【一个简单的SSO统一登录设计】
??

    推荐阅读