【用户访问鉴权】Openresty实现方案

在搭建中小型业务系统时,访问Token的鉴权认证往往与后台服务紧密关联。考虑采用Openresty设计一套业务后台无感知鉴权的流程方案,让后台开发人员能够专注于业务逻辑开发,无需担忧鉴权逻辑的设计。

主要借助Openresty自带的lua脚本模块来实现拦截鉴权逻辑。通过这种途径,可以将鉴权逻辑从后台服务中分离出来,实现鉴权的独立处理,进而简化后台服务的复杂度,提升系统的整体性能与可维护性。

业务系统的架构一般情况下如下图所示

(一)用户登录的认证流程

首先梳理一下登录流程,逻辑流程如下:

1. 请求登录接口

当进行前端页面登录接口的请求时,网关不会直接给予免认证放行,而是运用接口签名校验方案。在请求登录接口时,需要携带签名(例如 Authorization: Signature <签名>),签名方式可选用 HMAC-SHA256。 采用 HMAC 签名方式时,一个简便的实现办法是在接口请求的 Header 里携带请求时间戳和 Nonce,同时在 Authorization 中携带 HMAC-SHA256(Host + URI + 时间戳 + Nonce) 的哈希消息码。

2. 网关拦截校验

Openresty 的 Lua 脚本会对所有请求进行拦截。若请求的是登录接口,会对时间的有效性、是否为重放请求以及 Authorization 签名的有效性进行校验。

3. 生成设备指纹

简易的设备指纹能够直接借助 ngx.md5(user-agent | accept - encoding | accept - language) 来生成。要是仅面向内部网络进行访问,不向外部互联网开放,可在 ngx.md5 里增添 remoteAddr。 把 X-Fingerprint 的值设定为 ngx.md5(user-agent | accept - encoding | accept - language),并且将其置于 Header 之中,传递给后端服务。 不过,当不同设备的网络特征极为相似(例如浏览器版本相同、网络环境一致)时,模块或许会生成相同的指纹。此实现方案的准确率大约为 60%,不适用于需要精准追踪用户的业务,不过对基础爬虫识别以及 CC 攻击防御能起到一定的功效。

4. 账号密码校验

当授权认证服务收到用户使用账号密码发起的登录请求后,会进行账号密码的校验、登录防爆破策略的校验(像账号密码输入错误的次数、验证码的发送间隔、验证码错误的次数、同一 IP 下多账号登录等情形),同时记录登录风险(比如同一账号频繁登录、常用 IP 发生变更、常用地理位置改变、登录时间出现变动等状况)。

5. sessionId及accessToken

在账号密码验证通过之后,授权认证服务会采用 JWT 方案来生成 sessionId 与 accessToken。可将用户 ID、设备指纹、生成时间以及过期时间录入 JWT 中,方便网关进行解析和校验。 sessionId:体现用户的登录状态,具有长期有效性(一般为 7 天或者更长时间),存放在安全的地方(例如 HttpOnly Cookie 或者本地存储),不参与常规请求,能减少被截获的风险。 accessToken:具备短期有效性(通常为 15 - 30 分钟),用于访问受保护的资源(如 API、用户数据)。直接嵌入请求头(例如 Authorization: Bearer ),尽管传输较为频繁,但泄露风险的时间窗口较小。 注意:若用户主动登出或者修改密码,授权认证服务端需主动注销缓存里的 sessionId 和 accessToken。

6. 登录成功

登录成功后,接口会返回 accessToken 供前端页面使用,并且会把 sessionId 写入 Cookie 中。前端页面拿到 accessToken 之后,就能够访问业务接口。

(二)用户访问业务系统的鉴权逻辑流程

前端页面请求业务接口认证流程步骤

1. 前端发起请求

当前端页面发起业务请求时,拦截器会统一携带 accessToken、Timestamp、Nonce 等信息。这些信息的携带形式如下。

js 复制代码
Authorization: Bearer <accessToken>
X-Nonce: <随机字符串>     # 防重放攻击
X-Timestamp: <请求时间戳> # 客户端时间戳
2. 网关拦截验证

网关会对所有业务服务请求进行拦截,并按以下步骤开展校验工作:

(1)时间戳校验:具体规则是将 X-Timestamp 与服务器时间进行比较,若两者误差超过 ±5 分钟,该请求将被拒绝。

(2)Nonce 防重放:检查缓存中是否存在该 Nonce,若存在,则认定此为重复请求。

(3)JWT 解析与验证:对 accessToken 进行解析,获取用户 ID、设备指纹以及过期时间。查看 JWT 是否已过期(依据 exp 字段判断);查询 Redis 缓存,确认 accessToken 是否存在且有效;核实 JWT 中的设备指纹与请求来源的指纹是否一致。

(4)认证通过:若 AccessToken 认证成功,会把 JWT 解析得到的用户 ID 设置到新的 Header(例如 X-User-ID)中,便于业务服务获取使用,随后将请求转发至业务服务。

3. 业务服务处理

业务服务会对请求进行处理,并返回相关的业务结果数据。

(三)AccessToken过期请求刷新逻辑流程

前端页面拦截器,在401响应后自动发起刷新accessToken请求,避免用户感知中断。请求刷新accessToken逻辑如下所示

前端页面请求刷新AccessToken流程步骤

1. 前端发起刷新Token请求

当页面前端发起刷新Token请求时,拦截器会统一携带 Cookie、Timestamp、Nonce 信息进行处理。

js 复制代码
Cookie: sessionId=<value>  # 会话标识
Headers:
  X-Nonce: <随机字符串>    # 防重放攻击
  X-Timestamp: <请求时间>  # 客户端时间戳
2. 网关拦截验证

(1)时间戳校验:具体规则是将 X - Timestamp 与服务器时间作比较,若两者误差超出 ±5 分钟,请求将被拒绝。

(2)Nonce 防重放:对缓存进行检查,查看其中是否存在该 Nonce,若存在,则认定为重复请求。

(3)Cookie 中 SessionId 检查:查看 Cookie 里是否存在 sessionId;验证 JWT 是否已过期(依据 exp 字段);查询 Redis 中是否有该 sessionId 的缓存记录;对比缓存中的设备指纹与当前请求的设备指纹是否相符。

(4)若认证通过,网关会解析 JWT 以获取用户 ID,并把请求转发至授权认证服务。

3. 重新生成AccessToken

授权认证服务会生成新的 accessToken(JWT,包含用户 ID、设备指纹,有效期为 30 分钟),将新的 Token 存入 Redis 缓存,再把新的 AccessToken 返回给前端。

相关推荐
咖啡八杯3 小时前
【无标题】
java·后端·设计模式
Rain5094 小时前
1.3. Next.js与Nest.js在AI数据分析中的角色
前端·javascript·人工智能·后端·数据分析·node.js·ai编程
techdashen4 小时前
Rust 项目进展月报:2026 年 1 月
开发语言·后端·rust
武子康4 小时前
调查研究-170 Vert.x 是什么?它和 Netty 到底是什么关系?一张图讲清 Java 异步技术栈选型
java·后端
m沐沐4 小时前
【计算机视觉】OpenCV 模板匹配银行卡数字识别---上
人工智能·后端·python·opencv·计算机视觉·pycharm·numpy
伊灵eLing4 小时前
GoLang 语言高级(1)
开发语言·后端·golang
掘金者阿豪4 小时前
PDO连金仓数据库,我把踩过的坑整理了一下(上篇)
后端
用户34232323763174 小时前
大规模采集架构——从单台网关到千点集群
后端
我登哥MVP5 小时前
SpringCloud 核心组件解析:服务链路追踪
java·spring boot·后端·spring·spring cloud·java-ee·maven