登录业务的演变
单一服务器的模式登录业务
要想区分来自不同用户请求的话,服务端需要根据客户端的请求确认其用户的身份,即身份验证。
在人机交互中,身份验证意味着要求用户登录才能访问某些信息。而为了确认身份,用户必须提供只有用户和服务器知道的信息(即身份验证因子),比如用户名/密码。
在web
环境下,由于HTTP
是无状态的,那么服务器应该如何记住登录状态呢?
常见的身份验证方案分为2类:
- 基于
Cookie/Session
的验证。 - 基于
Token
的验证。
基于Cookie/Session
的验证
由于 HTTP
是一种无状态的协议,客户端每次发送请求时,首先要和服务器端建立一个连接,在请求完成后又会断开这个连接。这种方式可以节省传输时占用的连接资源,但同时也存在一个问题:每次请求都是独立的,服务器端无法判断本次请求和上一次请求是否来自同一个用户,进而也就无法判断用户的登录状态。
Cookie的工作原理
为了解决HTTP
无状态的问题,Lou Montnull
在1994年的时候,推出了Cookie
。
Cookie
指的就是在Cookie
是服务端发送给客户端的一段特殊信息,这些信息以文本的方式存放在客户端,客户端每次向服务器端发送请求时都会带上这些特殊信息,Cookie
仅仅是浏览器实现的一种数据存储功能。
注意:Cookie
功能需要浏览器的支持。如果浏览器不支持 Cookie(如大部分手机中的浏览器)或者把 Cookie
禁用了,Cookie
功能就会失效。不同的浏览器采用不同的方式保存 Cookie
。IE 浏览器会以文本文件形式保存,一个文本文件保存一个 Cookie
。
有了Cookie
之后,服务端就能够获取到客户端传递过来的信息了,如果需要对信息进行验证,还需要通过Session
。
客户端请求服务器,服务器端会为这次请求开辟一块内存空间,这个便是Session
对象。
Seesion的工作原理
Session
是另一种记录客户状态的机制,不同的是 Cookie
保存在客户端浏览器中,而 Session
保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是 Session
。客户端浏览器再次访问时只需要从该 Session
中查找该客户的状态就可以了。
如果说 Cookie
机制是通过检查客户身上的 “通行证” 来确定客户身份的话,那么 Session
机制就是通过检查服务器上的 “客户明细表” 来确认客户身份。
session
也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的 “身份标识”,然后客户端每次向服务器发请求的时候,都带上这个 “身份标识”,服务器就知道这个请求来自于谁了。对于浏览器客户端,大家都默认采用 cookie
的方式,保存这个 “身份标识”。这也就是 Cookie + Session
登录。
服务器使用 session
把用户的信息临时保存在了服务器上,用户离开网站后 session
会被销毁。这种用户信息存储方式相对 cookie
来说更安全。
可是 session
有一个缺陷:如果 **web**
服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候 **session**
会丢失。
注意:Session
的使用比 Cookie
方便,但是过多的 Session
存储在服务器内存中,会对服务器造成压力。
Session与Cookie的联系
cookie
数据存放在客户的浏览器上,session
数据放在服务器上。cookie
不是很安全,别人可以分析存放在本地的cookie
并进行cookie
欺骗,考虑到安全应当使用session
。session
会在一定时间内保存在服务器上。当访问增多,会比较占用你的服务器性能。考虑到减轻服务器性能方面,应当使用cookie
。- 单个cookie在客户端的限制是3k,就是说一个站点在客户端存存放的
cookie
不能超过3k。
Cookie与Session的实现流程
基于sesiion
的方案中,登录成功后,服务端将用户的身份信息存储在Session
里,并将session ID
通过cookie
传递给客户端。后续的数据请求都会带上cookie
,服务端根据cookie
中携带的session id
来得到辨别用户身份。
代码演示:
//....
session.setAtrrbuite("user",user);
//...
session.getAttrbuite("user");
//...
Cookie+Seesion存在的问题
虽然我们使用Cookie+Session
的方式完成了登录验证,担仍然存在一些问题:
- 由于服务端需要对接大量的客户端,也就需要存放大量的
Seesion ID
,这样就会导致服务器压力过大。如果服务器是个集群,为了同步登录的状态,需要将Session ID
同步到每一台服务器上,无形中增加了服务器端的维护成本。 - 由于
Session ID
存放在Cookie
中,所以无法避免CSRF
攻击(跨站请求伪造)。
为了解决Session+Cookie
机制暴露出的诸多问题,我们可以使用Token
的方式。
基于 token 的登陆认证
在大多数的使用Web API的互联网公司,Token
是多用户处理认证的最佳方式。
一下几点特性会让你在程序中使用基于Token的身份验证
- 无状态、可扩展。
- 支持移动设备。
- 跨程序调用。
- 安全。
Session
方案中用户信息(以Session
记录形式)存储在服务端。而Token
方案中(以Token
形式)存储在客户端,服务端仅验证Token
合法性。这种区别在单点登录(SSO
,Single Sign On
)的场景最为明显:
- 基于
Session
的SSO
:考虑如何同步Session
和共享Cookie
。比如登录成功后把响应Cookie
的domain
设置为通配兄弟应用域名的形式,并且所有都从身份验证服务同步Session
。 - 基于
Token
的SSO
:考虑如何共享Token
。比如进入兄弟应用时通过URL带上Token
。
基于Token的验证原理
基于Token的身份验证是无状态的,不将用户信息存在服务器中。这种概念解决了在服务端存储信息时的许多问题。NoSession意味着你的程序可以根据需要去增减机器,而不用去担心用户是否登录。
Token验证实现流程
而在基于Token
的方案中,服务端根据用户身份信息生成Token
,发放给客户端。客户端收好Token
,并在之后的数据请求带上Token
,服务端接到请求后校验并解析Token
得出用户身份,过程如下:
把这个过程分为两步,步骤图及解析如下:
用户首次登录时:
- 用户输入用户名和密码,并发送请求,访问
a.com/pageA
。 - 服务器端程序验证,账户密码无误,创建
Token
。 - 服务器端程序返回一个带签名的
Token
给客户端,客户端存储Token
。
后续页面访问时:
- 客户端每次访问
a.com/pageB
都携带第一次登录时获取的Token到服务器端。 - 服务端验证
Token
,校验成功则返回请求数据,校验失败则返回错误码。
Token的优势/特点
- 无状态、可扩展
在客户端存储的Token是无状态的,并且能够被扩展。基于这种无状态的和不存储Session
信息,所以不会对服务器端造成压力,负载均衡器能够将用户信息从一个服务器传到其他服务器上,即使是服务器集群,也不需要增加维护成本。
- 安全性
请求中发送Token而不是发送Cookie,能够防止CSRF(跨站请求伪造)。即客户端使用Cookie存储了Tooken,Cookie也仅仅是一个存储机制而不是用于认证。不将信息存储在Session中,让我们少了对Session的操作。Token也可以存放在前端任何地方,可以不用保存在Cookie中,提升了页面的安全性。
Token是有失效的,一段时间之后用户需要重新验证。
- 可扩展性
Token能够创建与其他程序权限的程序。
- 多平台跨域
用户名/密码属于知识因子,另外还有占有因子和遗传因子:
- 知识因子:用户登录必须知道的东西都是知识因子,比如用户名、密码等。
- 占有因子:用户登录时必须具备的东西,比如密码令牌、ID卡等。
- 遗传因子:个人的生物特征,比如指纹、虹膜、人脸等。
身份验证中的Token
就像身份证,由服务端签发/验证,并且在有效期内都具有合法性,**认证(Token)不认“人”(用户)**
。
Session
方案中用户身份信息(以Session
记录形式)存储在服务端。而Token
方案中(以Token
形式)存储在客户端,服务端仅验证Token
合法性。
具体的Token
的原理和使用:
https://blog.csdn.net/weixin_46487176/article/details/124139089?spm=1001.2014.3001.5501
分布式集群部署下的登录业务
随着微服务的架构的到来,大多应用采用分布式集群部署,不同服务一般部署在不同的服务器中,如何才能达到在其中某一个服务登录之后,在其他服务不必再次登录的效果?这就诞生了单点登录SSO
(Single Sign on
)这个概念。
单点登录,简称SSO
,是比较流行的企业业务整合的解决方案之一。SSO
的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
单点登录具有身份信息独立管理,更好地使用于分布式管理的优点。但是同时其缺点认证服务器的访问压力较大。
例如:如果引入集群的概念,单应用可能重新部署在3
台Tomcat
以上服务器,使用Nginx
来实现反向代理。
但是增加新的服务器之后,不同的服务器之间的Session ID
是不一样的,可能在A
服务器上登录成功了,能从服务器的Session
中获取用户信息,但是在B服务器上却查不到Session
信息,只好退出来继续登陆,结果A
服务器中的Session
因为超时失效,登陆之后又被强制退出来重新要求登陆。
所以不得不考虑多服务器之间的session数据一致性的问题,这就是单点登录的最早来源。
单点登录实现的方式
利用Session广播(Session共享)
单点登录的本质就是在多个应用系统中共享登录状态,如果用户的登录状态是记录在session
中的,要实现共享登录状态,就要先共享session
--利用session广播(session共享)实现。
更加具体的解决方案就是使用Redis
,就是把原本存储在不同服务器的上的session
拿出来放在一个独立的服务器上,之后取用session
也从该独立的session
服务去取,这样也就实现了session
的共享机制。
Cookie+Redis实现
使用cookie
和Redis
实现,要求在项目任何一个服务(模块)进行登录后,把用户数据放到两个地方。一个是cookie
,一个是Redis
。
- redis中存放包含登录用户信息的键值对(key:value),例如键值key可以采用用户的ID或者登录的IP等,value一般采用用户相关的数据,用户名等等。
- cookie中则是放入与上述存入redis一致的键值。
- 逻辑:
当用户在其中一个服务(模块)中登录后,在redis
和cookie
都有相关数据,当用户访问另一服务时,发送的请求会携带登录后存在的cookie
,服务通过cookie
值到redis
中进行查询得到数据,说明该用户已经登录,如果未查到则可以跳转到登录页面。
使用Token实现
https://github.com/bearbrick0/SpringSecurityOauth2_demo
- 客户端使用用户名跟着密码请求登录。
- 服务端收到请求,去验证用户名与密码。
- 验证成功后,服务端会签发一个
Token
,在把这个Token
发送给客户端。 - 客户端收到
Token
以后可以把它存储起来,比如放在Cookie
里。 - 客户端每次向服务端请求资源的时候需要带着服务端签发的
Token
。 - 服务端收到请求,然后去验证客户端请求里面带着的
Token
,如果验证成功,就向客户端返回请求的数据。
https://www.jianshu.com/p/3943e9fd3cca
https://www.jianshu.com/p/007f61e925df