单点登录(Single Sign On)
以server群如何生成、验证ID的方式大致分为两种:
“共享Cookie”
本质上cookie只是存储session-id的介质,session-id也可以放在每一次请求的url里。
SSO-Token方式因为共享session的方式不安全,所以我们不再以session-id作为身份的标识。我们另外生成一种标识,把它取名SSO-Token(或Ticket),这种标识是整个server群唯一的,并且所有server群都能验证这个token,同时能拿到token背后代表的用户的信息。
用户登录成功拿到token(或者是session-id)后怎么让浏览器存储和分享到其它域名下?同域名很简单,把token存在cookie里,把cookie的路径设置成顶级域名下,这样所有子域都能读取cookie中的token。这就是共享cookie的方式
跨域的时候怎么办
要实现SSO,需要以下主要的功能:
所有应用系统共享一个身份认证系统。
统一的认证系统是SSO的前提之一。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行效验,判断其有效性。
所有应用系统能够识别和提取ticket信息
要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。
单点登录
有一个独立的认证中心,只有认证中心才能接受用户的用户名和密码等信息进行认证,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,当用户提供的用户名和密码通过认证中心认证后,认证中心会创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌即得到了授权,然后创建局部会话。
当用户还没进行用户登录的时候
用户去访问系统1的保护资源 ,系统1检测到用户还没登录,跳转至SSO认证中心,SSO认证中心也发现用户没有登录,就跳转到用户至认证中心的登录页面
用户在登录页面提交用户相应信息后,认证中心会校验用户信息,如果用户信息正确的话认证中心就会创建与该用户的全局会话(全局会话过期的时候,用户就需要重新登录了。全局会话中存的信息可能有令牌,用户信息,及该在各个系统的一些情况),同时创建授权令牌,然后进行下一步,否则认证中心给出提示(用户信息有误),待用户再次点击登录的时候,再一次进行校验用户信息
认证中心带着令牌跳转到用户最初请求的地址(系统1),系统1拿到令牌后去SSO认证中心校验令牌是否有效,SSO认证中心校验令牌,若该令牌有效则进行下一步
注册系统1,然后系统1使用该令牌创建和用户的局部会话(若局部会话过期,跳转至SSO认证中心,SSO认证中心发现用户已经登录,然后执行第3步),返回受保护资源
用户已经通过认证中心的认证后
用户访问系统2的保护资源,系统2发现用户未登录,跳转至SSO认证中心,SSO认证中心发现用户已经登录,就会带着令牌跳转回系统2,系统2拿到令牌后去SSO认证中心校验令牌是否有效,SSO认证中心返回有效,注册系统2,系统2使用该令牌创建与用户的局部会话,返回受保护资源。
如果系统1的局部会话存在的话,当用户去访问系统1的保护资源时,就直接返回保护资源,不需要去认证中心验证了
局部会话存在,全局会话一定存在;全局会话存在,局部会话不一定存在;全局会话销毁,局部会话必须销毁
如果在校验令牌过程中发现客户端令牌和服务器端令牌不一致或者令牌过期的话,则用户之前的登录就过期了,用户需要重新登录
单点注销
在一个子系统中注销,全局会话也会被注销,所有子系统的会话都会被注销
用户向系统1发出注销请求,系统1根据用户与系统1建立的会话id从会话中拿到令牌,向SSO认证中心发起注销请求,认证中心校验令牌有效,会销毁全局会话,同时取出此令牌注册的系统地址,认证中心向所有注册系统发出注销请求,各系统收到注销请求后销毁局部会话,认证中心引导用户跳转值登录页面。
整体陈述
单点登录涉及SSO认证中心与多个子系统,子系统与SSO认证中心需要通信(交换令牌、校验令牌及发起注销请求等),子系统中包含SSO的客户端,SSO认证中心是服务端
认证中心与客户端通信可通过 httpClient、web service、rpc、restful api(url是其中一种) 等实现
客户端与服务器端的功能
客户端:
拦截子系统未登录用户请求,跳转至sso认证中心
接收并存储sso认证中心发送的令牌
与服务器端通信,校验令牌的有效性
建立局部会话
拦截用户注销请求,向sso认证中心发送注销请求
接收sso认证中心发出的注销请求,销毁局部会话
服务器端:
验证用户的登录信息
创建全局会话
创建授权令牌
与客户端通信发送令牌
校验客户端令牌有效性
系统注册
接收客户端注销请求,注销所有会话
实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性,因此要点也就以下两个:
存储信任
验证信任
以Cookie作为凭证媒介
最简单的单点登录实现方式,是使用cookie作为媒介,存放用户凭证。
用户登录父应用之后,应用返回一个加密的cookie,当用户访问子应用的时候,携带上这个cookie,授权应用解密cookie并进行校验,校验通过则登录当前用户。
不难发现以上方式把信任存储在客户端的Cookie中,这种方式很容易令人质疑:
Cookie不安全
不能跨域实现免登
对于第一个问题,通过加密Cookie可以保证安全性,当然这是在源代码不泄露的前提下。如果Cookie的加密算法泄露,攻击者通过伪造Cookie则可以伪造特定用户身份,这是很危险的。
对于第二个问题,更是硬伤。
通过JSONP实现
对于跨域问题,可以使用JSONP实现。
用户在父应用中登录后,跟Session匹配的Cookie会存到客户端中,当用户需要登录子应用的时候,授权应用访问父应用提供的JSONP接口,并在请求中带上父应用域名下的Cookie,父应用接收到请求,验证用户的登录状态,返回加密的信息,子应用通过解析返回来的加密信息来验证用户,如果通过验证则登录用户。
这种方式虽然能解决跨域问题,但是安全性其实跟把信任存储到Cookie是差不多的。如果一旦加密算法泄露了,攻击者可以在本地建立一个实现了登录接口的假冒父应用,通过绑定Host来把子应用发起的请求指向本地的假冒父应用,并作出回应。
因为攻击者完全可以按照加密算法来伪造响应请求,子应用接收到这个响应之后一样可以通过验证,并且登录特定用户。
通过页面重定向的方式
最后一种介绍的方式,是通过父应用和子应用来回重定向中进行通信,实现信息的安全传递。
父应用提供一个GET方式的登录接口,用户通过子应用重定向连接的方式访问这个接口,如果用户还没有登录,则返回一个的登录页面,用户输入账号密码进行登录。如果用户已经登录了,则生成加密的Token,并且重定向到子应用提供的验证Token的接口,通过解密和校验之后,子应用登录当前用户。
这种方式较前面两种方式,接解决了上面两种方法暴露出来的安全性问题和跨域的问题,但是并没有前面两种方式方便。
P3P协议
P3P是一种被称为个人隐私安全平台项目(the Platform for Privacy Preferences)的标准,能够保护在线隐私权,使Internet冲浪者可以选择在浏览网页时,是否被第三方收集并利用自己的个人信息。如果一个站点不遵守P3P标准的话,那么有关它的Cookies将被自动拒绝,并且P3P还能够自动识破多种Cookies的嵌入方式。p3p是由全球资讯联盟网所开发的。
我们在访问A网站时,理论上说,我们只能把Cookie信息保存到A站域名下,而不能写入到B网站下。如果想要跨域读写Cookie,只是通过script标签变相访问B网站在一些浏览器是行不通的,此时B网站的服务器应该告诉浏览器允许A网站写入Cookie,否则浏览器将会拒绝执行,这就是P3P协议。
服务端如何告诉浏览器?
P3P提供了一种简单的方式 ,来加载用户隐私策略,只要在http响应的头信息中增加 response.setHeader(“P3P”,”CP=NON DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONa HISa TELa OTPa OUR UNRa IND UNI COM NAV INT DEM CNT PRE LOC);而无需指定隐私策略文件也可以达到指定隐私策略的目的。 CP=后面的字符串分别代表不同的策略信息。
总结
因为P3P协议所以不能保证所有浏览器都能通过script标签方式跨域写Cookie,有的浏览器本身就是拒绝跨域的。
.Jsonp的方式#
跨域Ajax请求在浏览器阶段就会被阻止,我们可以通过script标签返回想要的json数据
这种方式,前端的回调函数和后端耦合度较高。前端可以在调用后端方法时带上回调函数名(?callback=xxxxx)
CORS简介#
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头文件。
跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。
GET跨域请求原理
当客户端浏览器发起一个跨域的HTTP请求,浏览器经过请求响应,如果没有看到Access-Control-Allow-Origin的header头部,会认为你的请求是不合法的。换句话说,我们只要在被请求的服务器上设置这个头部,浏览器就会允许我们进行请求。
解决方法
对于简单的请求,我们直接在服务端 设置就可以了。如图,只要请求的地址是www.a.com就会被浏览器允许跨域。如果想要允许对于多个来源可以用,号进行隔开;如果想要允许所有来源,设置为*就可以,不过建议不要使用,这样会造成安全隐患。
CORS流程
请求发起时,浏览器先判断当前是否是跨域的AJAX;
如果是,判断是否是普通类型请求(GET类型,无自定义头数据);
普通请求,直接发起GET到服务端,在响应头中寻找 Access-Contro-Alow- Origin,如果有且允许,处理响应结果;
不是普通请求(非GET类型,或有自定义头), 先 PreFlight(即发起一个 method= OPTIONS)的请求,
要求返回 Access-Control-Allow- Methods和 Access-Control-Allow- Headers, 内容体为空
PreFlight正确执行后, 再发起GET请求, 获得响应结果, 并处理结果.
JSON Web Token简述
JWT是一个开放的标准(RFC 7519),它定义了一个紧凑且自包含的方式,用于在各方之间以JSON对象安全地传输信息.这些信息可以通过数字签名进行验证和信任。可以使用秘密(使用HMAC算法)或使用RSA的公钥/私钥对来对JWT进行签名。
紧凑:由于它们尺寸较小,JWT可以通过URL,POST参数或HTTP头内发送。另外,尺寸越小意味着传输速度越快。
自包含:有效载荷包含有关用户的所有必需信息,避免了多次查询数据库的需要。
JSON Web Token 应用场景
身份验证(Authentication):这是使用JWT最常见的情况。一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由,服务和资源。单点登录是目前广泛使用JWT的一项功能,因为它的开销很小,而且能够轻松地在不同的域中使用。
信息交换:JSON Web Tokens是在各方之间安全传输信息的好方法。因为JWT可以被签名,例如使用公钥/私钥对,所以你可以确定发件人是他们说的那个人。此外,由于使用标头、有效载荷、计算签名,因此您还可以验证内容是否未被篡改。
JWT 结构
JSON Web Tokens包含三个由点(.)分隔的部分,它们是:头部、有效载荷、签名
JWT通常看起来如下所示。
xxxxx.yyyyy.zzzzz
Header头部
头部通常由两部分组成:令牌的类型(即JWT)和正在使用的散列算法(the hashing algorithm 如HMAC SHA256或RSA)。
例如:
{
“alg”: “HS256”,
“typ”: “JWT”
}
然后,这个JSON被Base64Url编码,形成JWT的第一部分。
有效载荷
令牌的第二部分是包含声明的有效载荷。 声明是关于实体(通常是用户)和附加元数据的声明。 有三种类型的声明:保留,公开和私有声明。
保留的声明(Reserved claims):这是一组预先定义的声明,不是强制性的,但推荐使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行人),exp(到期时间),sub(主题),aud(听众)等等。
请注意,声明名称只有三个字符长,因为JWT是紧凑的。 公开声明(Public claims):这些可以由使用JWT的人员任意定义。 但为避免冲突,应在IANA JSON Web令牌注册表中定义它们,或者将其定义为包含防冲突命名空间的URI。
私有声明(Private claims):这是为了同意使用它们的各方之间共享信息而创建的自定义claims。
有效载荷的一个例子可以是:
{
“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}
然后将有效载荷Base64Url进行编码,以形成JSON Web令牌的第二部分。
签名
要创建签名部分,您必须采用编码头部,编码有效载荷,密钥,头部中指定的算法并签名。
例如,如果您想使用HMAC SHA256算法,签名将按以下方式创建:
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)
签名用于验证JWT的发件人是谁,并确保邮件在一路上没有改变。
拼接在一起
输出是三个由点分隔的Base64字符串,可以轻松地在HTML和HTTP环境中传递,而与基于XML的标准(如SAML)相比,它更加紧凑。
下面显示了一个JWT,它具有先前的头部和有效载荷编码,并且用秘密签名。
基于Go语言实现的单点登录系统
JSON Web Token如何工作
在身份验证中(authentication),当用户使用他们的凭证(credentials)成功登录时,将返回一个JSON Web Token,并且必须保存在本地(通常在本地存储中,但也可以使用Cookie),而不是在传统方法中创建会话 服务器并返回一个cookie。
无论何时用户想要访问受保护的路由或资源,用户代理都应发送JWT,通常在Authorization头部的Bearer模式中。 头部的内容应该如下所示:
Authorization: Bearer
这是一种无状态身份验证机制,因为用户状态永远不会保存在服务器内存中。 服务器的受保护路由将在授权头中检查有效的JWT,如果存在,则允许用户访问受保护的资源。 由于JWT是独立的,所有必要的信息都在那里,减少了多次查询数据库的需求。
这使您可以完全依赖无状态的数据API,甚至向下游服务发出请求。 无论哪个域正在为您的API提供服务,跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。
为什么我们应该使用JWT
让我们来谈谈与简单Web令牌(SWT)和安全声明标记语言令牌(SAML)相比,JSON Web令牌(JWT)的好处。
由于JSON不如XML冗长,所以在进行编码时,它的大小也会变小,从而使JWT比SAML更紧凑。这使得JWT成为在HTML和HTTP环境中传递的一个很好的选择。
安全方面,SWT只能通过使用HMAC算法的共享秘密进行对称签名。但是,JWT和SAML令牌可以使用X.509证书形式的公钥/私钥对进行签名。与签署JSON的简单性相比,使用XML数字签名签署XML而不引入模糊的安全漏洞是非常困难的。
JSON解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象映射。这使得使用JWT比SAML断言更容易。
关于使用情况,JWT在互联网上使用。这突出显示了在多个平台(尤其是移动平台)上JSON Web令牌的客户端处理的简易性。