目录大纲
HTTP 基本认证
基本认证(Basic access authentication)是 HTTP 中用来允许网页浏览器或其他客户端程序在请求时提供用户名及口令形式身份凭证的一种登录验证方式
核心概念
Basic 认证通过核对用户名、密码的方式,来实现用户身份的验证,认证的关键要素如下:
-
userId: 用户名
-
password:用户密码
-
realm:领域【不同领域资源可能需要不同身份认证】
认证过程
- 客户端访问受限资源
/private_resource
GET /private_resource HTTP/1.1
Host: localhost
- 服务端返回
401 Unauthorized要求身份认证
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm=private_resource
响应头部通过 WWW-Authenticate 字段告知客服端 => Basic 表示:认证方案为基本认证 、realm 值表示:认证的领域范围
WWW-Authenticate: Basic realm=<需要保护资源的范围>
说明:Response 中的 WWW-Authenticate 字段会 自动指示 浏览器弹出询问 用户名 和 密码 提示框
- 客户端发送认证请求
客服端会将用户输入的账号和密码进行 base64 编码并作为 Authorization 的值,然后再次向服务器端发送认证请求
GET /private_resource HTTP/1.1
Authorization: Basic b3JhbmdlOjEyMzQ1Ng==
Authorization 首部的格式为Basic base64(userId:password)。实际代码如下:
Buffer.from('orange:123456').toString('base64'); // b3JhbmdlOjEyMzQ1Ng==
- 服务器端验证请求
- 依据客户端的请求,确定资源对应的领域范围
realm - 解析
Authorization请求头部,获得用户名、密码 - 判断用户是否有访问该
realm资源的权限 - 验证用户名、密码是否匹配。验证通过,则返回请求资源;否则,返回
401要求重新认证或者返回403禁止访问
优缺点及适用范围
- 优点:简单,被广泛支持
- 缺点:
- 内容安全:用户名和密码是以明文传输的【使用的
base64编码很容易反解】,而且过程中对于中间人的攻击是无法防止的 - 重放攻击:即使认证内容无法解码,恶意用户可以在获取了认证内容后使用它不断的向服务器发起请求
- 内容安全:用户名和密码是以明文传输的【使用的
- 适用:
- 内部网络,或者对安全要求不是很高的网络
- 结合
HTTPS进行使用(https保证网络的安全性,而基本认证做的只是客户端身份的识别 )
Session-Cookie 认证
Session-Cookie 认证是利用服务端的 Session(会话)和浏览器(客户端)的 Cookie 来实现的前后端通信的一种认证方式
背景
由于 HTTP 请求是无状态的,所以服务端正常情况下是无法辨识请求发送者身份的,因此我们就需要在服务端创建 Session 会话来记录状态,将相同客户端的请求都维护在各自的会话记录中,每当用户请求到达服务端时,先校验请求中的用户标识是否存在于 Session 中,如果有则表示已经认证成功,否则表示认证失败。
认证过程
- 客服端向服务器端发送请求,首次请求时服务器端会自动创建
Session(将Session保存在 内存 或Redis中),并给这个Session生成一个 唯一 的身份标识凭证session_id(通常称为sid),然后在响应头Set-Cookie中设置这个唯一标识符 - 客服端收到响应请求,解析响应头并自动将
sid保存在本地Cookie中,后面浏览器发送请求时会自动附带上该域名下的Cookie信息 - 服务端在接收客户端请求时会去解析请求头
Cookie中的sid,依据对应sid判断请求是否合法并给出相应的响应信息 - 用户登出,服务端和浏览器将会同时销毁各自保存的会话
ID,服务端会根据数据库验证会话身份凭证,如果验证通过,则进行销毁处理
注意 ⚠️: Cookie 是仅在浏览器中存在的报文字段,诸如移动原生 APP 是无法解析存储 Cookie 请求/响应头的
优缺点及适用范围
-
优点
Cookie简单易用,在不受用户干预或过期处理的情况下,Cookie通常是客户端上持续时间最长的数据保留形式Session数据存储在服务端,相较于 JWT 方便进行管理,也就是当用户登录和主动注销时,只需要添加/删除对应的Session就可以了
-
缺点
- 非常不安全,
Cookie将数据暴露在浏览器中,增加了数据被盗的风险【容易被 CSRF 等攻击】 Session存储在服务端,增大了服务端的开销,用户量大的时候会大大降低服务器性能- 用户认证后,服务端做认证记录,如果认证的记录被保存在 内存 中,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权资源,这样在分布式的应用上,相应的限制了负载均衡的能力,也意味着限制了应用的扩展能力
- 非常不安全,
-
适用
- 会话状态管理(如用户登录状态、购物车等需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为追踪(如跟踪分析用户行为等)
Token 认证
背景
当使用 session-cookie 认证时,需要将 session 存储在服务端,这样的话不仅会增大服务端的开销,而且也不利于分布式应用扩展。而基于 Token 的身份验证是无状态的,我们不将用户信息存在服务器或 Session 中,而是通过加解密的方式进行认证判断。
认证过程
-
用户输入登录信息并进行登录请求
-
服务端在收到请求后,验证用户输入的登录信息
-
验证成功后,服务端会
签发一个Token(通常包含用户 基础信息、权限范围 和 有效时间 等),并把这个Token返回给客户端 -
客户端收到
Token后需要把它存储起来,比如存放在webStorage里【一般不放Cookie,因为可能会有跨域问题以及安全性问题 CSRF 】 -
后续客户端每次向服务端请求资源的时候,将
Token附带于HTTP请求头Authorization字段中发送请求 -
服务端收到请求后,拦截所用请求并校验客户端请求中
Token,如果验证成功,就向客户端返回请求的数据,否则拒绝返回
优缺点及适用范围
- 优点
- 无状态、可扩展:
Token机制在服务端不需要存储会话session,因为Token自身包含了其所标识用户的相关信息【服务器既是加密者亦是解密者】,而且这也有利于在多个服务间共享用户状态 - 安全性:有效避免
CSRF攻击(因为不需要Cookie),并且我们也通过token revocataion使一个特定的token或是一组有相同认证的token无效 - 多平台支持;支持跨域跨程序调用【因为
Cookie是不允许跨域访问的,而Token则不存在这个问题】
- 无状态、可扩展:
- 缺点
- 占带宽:正常情况下比
sid更大,消耗更多流量,挤占更多宽带 - 性能问题:相比较于
Session-Cookie认证来说,Token需要服务端花费更多时间和性能来对Token进行 解密验证,其实Token相较于Session-Cookie来说就是一个时间换空间的方案
- 占带宽:正常情况下比
Token 过期与 Refresh Token
Token 过期
Token 是访问特定资源身份认证凭证,出于安全性及便捷性考虑会设置相应的 过期时间,不过一般设置的时间不会过长
Refresh Token
为什么需要 refresh token ?
出于安全性考虑 token 会设置对应的过期时间,而当 token 过期后需要重新获取【重复登录、授权等流程再次获取 token 显然不是很友好】,于是就有了 refresh token。这样的话,我们可以在 refresh token 有效期内通过 refresh token 来获取新的 token。
Refresh Token 也是加密字符串,并且和 token 是相关联的,但与获取资源的 token 不同。refresh token 的作用仅仅是获取新的token,因此其作用和安全性要求x相对较低,因此它的过期时间也可以设置得长一些,可以以天为最小单位。当然如果 refresh token 过期了,还是需要重新走登录验证流程操作的
Token 认证常见问题及解决方案
注销登录
如果用户改变【如:退出登录、修改密码、注销等】,对于之前未过期的 token 如何废弃处理?
正如我们所了解的, token 在服务器端是无状态的,因此服务器是无法知道哪些未过期的 token 是被废弃的 。
针对这个问题下面给出一些解决方案供参考:
解决方案:
- 黑名单机制: 服务器维护一份黑名单,在用户改变操作的时,将当前
token加入到黑名单中;每次请求时判断token是否存在于黑名单来验证它是否有效,如果后面token过期,则将其移除黑名单列表。 - 内存数据库存储: 将
token存储于 Radis 等内存数据库中,不过在每次校验token时需要判断其是否存在于库中,这样的话就类似于session-cookie认证了,显然违背了token认证无状态的原则
续签问题
说明:
token 认证时,出于安全考虑,基本都是会设置对应的过期时间【过期时间一般比较短】。而对于 token 过期之后如果去操作认证也是一个问题?
token失效后可以跳转至登录界面让用户重新登录获取新的token; 而由于token过期时间比较短,这种频繁登陆的操作交互显然不是很友好。token失效后可以通过refreshToken来获取新的token,而refresh token的过期时间相对而言较长,这样可以确保在token过期后无痛感刷新token数值。
refresh token 刷新的不足:
- 用户注销的时候需要同时保证两个
token都无效 - 通过
refreshToken重新请求获取token的过程中会有短暂token不可用的情况【可以通过在客户端设置定时器,当token快过期的时候,提前去通过refreshToken获取新的token】
单点登录(SSO)
单点登录(Single Sign On),简称为 SSO,是比较流行的业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
背景
早期时一个系统基本就可以满足所有的业务需求,用户只需使用账号和密码登录认证即可。但是随着业务的迭代发展,系统架构会随之迭代,演变越来越多的子系统。这对于用户而言,在每进入一个系统时可能都需要进行一次登录才能获取相应资源的访问权限。因此,为了解决此问题,便有了 单点登录,即:在一个多系统共存的环境下,用户在一处登录后,就可以访问所有相互信任的应用系统。
认证过程
同域 SSO
场景: 通常情况下企业拥有属于自己的域名,而我们可以通过二级域名区分不同的子系统。如:企业域名:orange.com,其业务系统 A、B 的域名分别为:a.orange.com 和 b.orange.com。而我们的单点登录系统假设为:sso.orange.com。
针对上面场景我们可以将 cookie 的域设置为顶域 orange.com,这样所有子域的系统都可以访问到顶域的 cookie。
步骤:
- 用户访问渍系统
a.orange.com时,如果没有登录则跳转至sso.orange.com系统进行登录。 - 登录认证成功后,将验证用户的信息通过加密后保存到主域名(在这里是
.orange.com)的cookie中,并跳到A子系统 - 当用户访问相同主域名下的系统
B时,此时由于A系统已经登录过了,存在对应主域名下的cookie,所有访问系统B的资源就无需在进行登录了,对应cookie就在B系统中的请求中自动携带。
跨域 SSO
场景: 对于系统是部署在不同的域名下,我们应如何去实现单点登录呢?如果是跨域的情况的话,那我们就需要了解单点登录的标准流程【CAS 流程】
CAS:(
Central Authentication Service) 中央授权服务,我们也可以称为SSO认证中心(CAS Server)
步骤:
- 用户访问子系统
A受保护资源,系统A发现用户未登录,跳转至SSO认证中心,并将自己的地址作为参数 SSO认证中心发现用户未登录,跳转到登录界面- 用户输入账号、密码或使用其他快捷等方式进行登录
SSO认证中心校验用户信息,创建用户与SSO认证中心之间的会话【全局会话】,同时创建授权令牌tokenSSO认证中心带着令牌token重定向参数的请求地址(系统A)- 系统
A拿到令牌token,去SSO认证中心校验是否有效。有效,则注册系统A - 系统
A使用该令牌创建与用户的会话,key为用户字段,值为token,称为局部会话,返回系统A受保护资源 - 用户访问子系统
B受保护资源,系统B发现用户未登录,跳转至SSO认证中心,并将自己的地址作为参数 SSO认证中心发现用户已登录,跳转回系统B的地址,并附上令牌token- 系统
B拿到令牌token,去SSO认证中心校验是否有效。有效,则注册系统B - 系统
B使用该令牌创建与用户的局部会话,返回系统B受保护资源
说明:
全局会话:用户与 SSO 认证中心建立的会话
局部会话:用户与各个子系统建立的会话
CSA 注销机制
- 子系统发起注销请求
- 依据用户与当前系统建立的会话
Id获取到的令牌token,向SSO认证中心发起注销请求 SSO认证中心校验token是否有效。有效,销毁全局会话,同时取出所有用此令牌注册的系统地址SSO认证中心向所有注册系统发起注销请求- 各注册系统接收
SSO认证中心的注销请求,销毁局部会话 SSO认证中心引导用户至登录页面