前端鉴权知识

492 阅读13分钟

目录大纲

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==
  • 服务器端验证请求
  1. 依据客户端的请求,确定资源对应的领域范围 realm
  2. 解析 Authorization 请求头部,获得用户名、密码
  3. 判断用户是否有访问该 realm 资源的权限
  4. 验证用户名、密码是否匹配。验证通过,则返回请求资源;否则,返回 401 要求重新认证或者返回 403 禁止访问

优缺点及适用范围

  • 优点:简单,被广泛支持
  • 缺点:
    • 内容安全:用户名和密码是以明文传输的【使用的 base64 编码很容易反解】,而且过程中对于中间人的攻击是无法防止的
    • 重放攻击:即使认证内容无法解码,恶意用户可以在获取了认证内容后使用它不断的向服务器发起请求
  • 适用:
    • 内部网络,或者对安全要求不是很高的网络
    • 结合 HTTPS 进行使用(https 保证网络的安全性,而基本认证做的只是客户端身份的识别 )

Session-Cookie 认证

Session-Cookie 认证是利用服务端的 Session(会话)和浏览器(客户端)的 Cookie 来实现的前后端通信的一种认证方式

背景

由于 HTTP 请求是无状态的,所以服务端正常情况下是无法辨识请求发送者身份的,因此我们就需要在服务端创建 Session 会话来记录状态,将相同客户端的请求都维护在各自的会话记录中,每当用户请求到达服务端时,先校验请求中的用户标识是否存在于 Session 中,如果有则表示已经认证成功,否则表示认证失败。

04d2f6a9-2f38-4bc0-97ff-27f92cb9cd02_.png

认证过程

  • 客服端向服务器端发送请求,首次请求时服务器端会自动创建 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 过期了,还是需要重新走登录验证流程操作的

a0f082b2-0817-4cb8-966d-977d5427ca73_.png

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,其业务系统 AB 的域名分别为:a.orange.comb.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)

d96498da-48e9-4eea-b288-2ba1c262fbd5_.png

步骤:

  • 用户访问子系统 A 受保护资源,系统 A 发现用户未登录,跳转至 SSO 认证中心,并将自己的地址作为参数
  • SSO 认证中心发现用户未登录,跳转到登录界面
  • 用户输入账号、密码或使用其他快捷等方式进行登录
  • SSO 认证中心校验用户信息,创建用户与 SSO 认证中心之间的会话【全局会话】,同时创建授权令牌 token
  • SSO 认证中心带着令牌 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 认证中心引导用户至登录页面

参考

一文读懂HTTP Basic身份认证

Token 认证的来龙去脉

编程概念精讲(二):单点登录(SSO)详解

前端开发登录鉴权方案完全梳理

前端鉴权的兄弟们:cookie、session、token、jwt、单点登录