单点登录
多系统,单一位置登录,实现多系统同时登录的一种技术。常出现在互联网应用和企业级平台中。
单点登录一般是用于互相授信的系统,实现单一位置登录,全系统有效的。
一、 Session跨域
所谓Session跨域就是摒弃了系统提供的Session,而使用自定义的类似Session的机制来保存客户端数据的一种解决方案。
如:通过设置cookie的domain来实现cookie的跨域传递。在cookie中传递一个自定义的session_id。这个session_id是客户端的唯一标记。将这个标记作为key,将客户端需要保存的数据作为value,在服务端进行保存(数据库保存或NoSQL保存)。这种机制就是Session的跨域解决。
什么是跨域:客户端请求的时候,请求的服务器,不是同一个ip,端口,域名,主机名的时候,都称为跨域。
什么是域:在应用模型中,一个完整的,有独立访问路径的功能集合称为一个域。如:百度称为一个应用或系统。百度下有若干的域,如:搜索引擎(www.baidu.com),百度贴吧(tie.baidu.com),百度知道(zhidao.baidu.com)等。域信息有时候也称为多级域名。
域的划分:以IP,端口,域名,主机名为标准,实现划分。
二、Spring Session共享
spring-session技术是spring提供的用于处理集群会话共享的解决方案。spring-session技术是将用户session数据保存到三方存储容器中,如:mysql,redis等。
spring-session技术是解决同域名下的多服务集群session共享的问题不能解决跨域session共享问题。
使用:
- 配置一个Spring提供的Filter,实现数据的拦截保存,并转换为spring-session需要的会话对象
- 必须提供一个数据库的表格信息(由spring-session提供,找spring-session-jdbc.jar/org/springframework/session/jdbc/*.sql)
- spring-session表:保存客户端session对象的表格
- spring-session-attributes表:保存客户端session中的attributes属性数据的表格
- spring-session框架,是结合Servlet技术中的HTTPSession完成的会话共享机制。在代码中是直接操作HttpSession对象的。
Nginx Session共享
nginx中的ip_hash技术能够将某个ip的请求定向到同一台后端,这样一来这个ip下的某个客户端和某个后端就能建立起稳固的session,ip_hash是在upstream配置中定义的,具体如下:
upstream nginx.example.com
{
server 127.0.0.1:8080;
server 127.0.01:808;
ip_hash;
}
正向代理:对客户端已知,对服务器透明的代理应用,称为正向代理。如:翻墙梯子。
反向代理:对服务端已知,对客户端透明的代理应用,成为反向代理。如:nginx
Nginx服务器一旦安装,一般提供7*24小时服务。建议安装在服务器中。
Nginx是一个C语言开发的应用服务器。可以提供的服务有:静态WEB服务(Apache http server),右键代理服务,虚拟主机,反向代理服务器。
Nginx应用体积非常的小,对CPU和内存的要求也很低。且对负载能力有非常好的体现。核心功能是应用自主开发,很多附属功能都是借助其它应用实现的,如:SSL协议的解析-opensll,perl库(正则)的解析-perl包实现的。
ip_hash是容易理解的,但是因为仅仅能用ip这个因子来分配后端,因此IP_hash是有缺陷的,不能在一些情况下使用:
nginx不是最前端的服务器。
ip_hash要求nginx一定是最前端的服务器,否则nginx得不到正确ip,就不能根据ip做hash。比如使用squid为最前端,那么nginx取ip时只能得到squid的服务器ip地址,用这个地址来作为分流肯定是错乱的。
nginx的后端还有其它方式的负载均衡。
假如nginx后端又有其它负载均衡,将请求又通过另外的方式分流了,那么某个客户端的请求肯定不能定位到同一台session应用服务器上。
Token机制
1 传统身份认证
HTTP是一种没有状态的协议,也就是它并不知道是谁访问应用。这里我们把用户看成客户端,客户端使用用户名、密码通过了身份验证,不过下回这个客户端再发送请求的时候,还得再验证一下。
解决方法就是,当用户请求登录的时候,如果没有问题,我们在服务端生成一条记录,这个记录里可以说明一下登录的用户是谁,然后把这条记录的ID号发送给客户端,客户端收到以后把这个ID号存储在Cookie里,下次这个用户再向服务端发送请求的时候,可以带着这个Cookie,这样服务端会验证一个这个Cookie里的信息,看看能不能在服务端这里找到对应的记录,如果可以,说明已经通过了身份验证,就把用户请求的数据返回给客户端。
上面说的就是Session,我们需要在服务端存储为登录的用户生成的Session,这些Session可能会存储在内存,磁盘,或者数据库里。我们可能需要在服务端定期的去清理过期的Session。
这种认证出现的问题是:
- Session:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加。
- 可扩展性:在服务端的内存中使用Session存储登录信息,伴随而来的是可扩展性问题。
- CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个让人头疼的问题。在使用Ajax抓取另一个域的资源,就可以会出现禁止请求的情况。
- CSRF(跨站请求伪造):用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用起访问其它的网站
这些问题中,可扩展性是最突出的
。因此我们有必要去寻找一种更为行之有效的方法。
2 Token身份认证
使用基于Token的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
- 客户端使用用户名、密码登录请求
- 服务端收到请求,去验证用户名、密码
- 验证成功后,服务端会签发一个Token,再把这个Token发送给客户端
- 客户端收到Token以后可以把它存储起来,比如放在Cookie里或者Local Storage里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的Token
- 服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据
使用Token验证的优势:
- 无状态,可扩展
- 在客户端存储的Tokens是无状态的,并且能够被扩展。基于这种无状态和不存储Session信息,负载均衡器能够将用户信息从一个服务传到其它服务器上。
- 安全性
- 请求中发送token而不再发送cookie能够放置CSRF(跨站请求伪造)。即使在客户端使用cookie存储token,cookie也仅仅是一个存储机制而不是用户认证。不将信息存储在Session中,让我们少了对Session操作。
JSON Web Token(JWT)机制
JWT是一种紧凑
且自包含
的,用于在多方传递JSON对象的技术。传递的数据可以使用数字签名增加其安全性。可以使用HMAC加密算法或RSA公钥/私钥加密方式。
- 紧凑:数据小,可以通过URL,POST参数,请求头发送。且数据小代表传输速度快。
- 自包含:使用payload数据块记录用户必要且不隐私的数据,可以有效减少数据库访问次数,提高代码性能。
JWT一般用户处理用户身份验证
、数据信息交换
。
- 用户身份验证:一旦用户登录,每个后续请求都将包含JWT,允许用户访问该令牌允许的路由,服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它的开销很小,并且能够轻松地跨不同域使用。
- 数据信息交换:JWT是一种非常方便的多方传递数据的载体,因为其可以使用数据签名来保证数据的有效性和安全性。
1 JWT数据结构
JWT的数据结构是:ABC。由字段点.
来分隔三部分数据
-
A:header头信息
-
B:payload(有效)
-
C:Signature(签名)
-
Header
头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA) 一个例子如下:
下边是Header部分的内容
{
"alg": "HS256", //加密算法(HMAC, RSA, SHA256)
"typ": "JWT" //类型,固定为JWT
}
将上边的内容使用Base64Url编码,得到一个字符串就是JWT令牌的第一部分。
- Payload
第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比 如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。
此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。 最后将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。 一个例子:
在payload数据块中一般用于记录实体(通常为用户信息)或其它数据。主要分为三个部分,分别是:已注册信息(registered claims),公开数据(public claims),私有数据(private claims)
。
公开数据部分一般都会在JWT注册表中增加定义。避免和已注册信息冲突。
公开数据和私有数据可以由程序员任意定义。
{
"sub": "1234567890",
"name": "456",
"admin": true
}
- Signature
- 第三部分是签名,此部分用于防止jwt内容被篡改。
这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。
一个例子:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload), secret
)
base64UrlEncode(header):jwt令牌的第一部分。
base64UrlEncode(payload):jwt令牌的第二部分。
secret:签名所使用的密钥。
执行流程
注意:使用JWT实现单点登录时,需要注意token时效性。token是保存在客户端的令牌数据,如果永久有效,则有被劫持的可能。token在设计的时候,可以考虑一次性有效或一段时间内有效。如果设置有效时长,则需要考虑是否需要刷新token有效期问题。
token保存位置:
使用JWT技术生成的token,客户端在保存的时候可以考虑cookie或localstorage。cookie保存方式,可以实现跨域传递数据。localStorage是私有的本地存储,无法实现跨域。
webstorage:
webstorage:可保存的数据容量为5M。且只能存储字符串数据。
webstorage分为localStorage和sessionStorage。
- localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据,否则数据永远不会消失。
- sessionStorage是会话相关的本地存储单元,生命周期是仅在当前会话下有效。sessionStorage引入了一个『浏览器窗口』的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会销毁同时独立的打开同一个窗口同一个也买呢,sessionStorage也是不一样的。