基于 Nginx 的异构后端统一鉴权服务🛡️
JWT authorization with NGINX Ingress Controller
背景知识
JWT 验证
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境中安全地传递信息。JWT 的设计目的是为了在各方之间安全地传输声明(claim),这些声明可以包含关于用户身份(ID)、权限或其他元数据的信息。
JWT 的结构
一个 JWT 通常由三部分组成,分别用点(.)分隔:
- 头部(Header),包含令牌的类型(通常是 “JWT”)和所使用的签名算法(如 SHA256、RSA、ECDSA)
- 负载(Payload),包含实际的数据(声明),这些声明可以是公开声明、私有声明或注册声明。
- 公开声明:可以包含在 JWT 中的标准化声明,例如 iss(发行者)、exp(过期时间)、sub(主题)等。
- 私有声明:用户自定义的数据,不是标准化的声明,需要双方协商一致。
- 注册声明:一些预定义的声明,用于在 JWT 中传递用户相关的基本信息。
- 负载部分的内容是 Base64Url 编码的 JSON 对象。
- 签名(Signature),由头部和负载部分的数据以及一个密钥经过特定算法(如 HMAC SHA256、RSA 等)生成,用于验证 JWT 的完整性。

无状态特性
JWT 包含了所有必要的用户身份和权限信息(如用户 ID、权限等),并通过签名保证数据的完整性。当客户端每次发送请求时,会将 JWT 令牌包含在请求头中,服务器通过验证该令牌来进行身份验证和授权,而不需要维护和查询服务器端的会话数据。服务器只需检查 JWT 的签名和有效性,而不需要存储任何会话数据。
安全性考虑
JWT(JSON Web Token)的数据通常 不被加密,而是 签名 来保证数据的完整性和真实性,签名的目的是确保 JWT 的数据没有被篡改。接收方使用相同的签名算法和密钥来验证签名是否匹配。如果不匹配,表明数据可能被篡改。
JWT 的头部(Header)和负载(Payload)部分一般 不加密,只进行 Base64Url 编码。这种编码只是将数据转换为 URL 安全的字符串形式,并不提供任何安全性,任何人都可以解码 JWT,查看其中的内容。这意味着敏感信息不应该直接存储在 JWT 的负载中,除非数据经过加密或采取了其他安全措施。
数据加密:如果需要在 JWT 中存储敏感信息,可以对数据进行加密后再放入负载中。这样,即使 JWT 被截获,未经授权的第三方也无法解密和查看数据。
使用 HTTPS:为了防止 JWT 在传输过程中被截获,应该使用 HTTPS 进行加密通信。
如果使用 JWE(JSON Web Encryption) 规范,可以对 JWT 进行加密,只有持有解密密钥的方能读取。
RBAC 模型
表设计
- users
- auth_user_roles
- auth_role
- auth_role_permissions
- auth_permission

限流组件
- 基于 Reids ZSet + Lua 实现的滑动窗口限流
排队服务也用到了这个限流组件,排队服务是特定接口请求的限流、鉴权服务是用户请求的限流
types.go
1 | package limiter |
redis_slide_window.go
1 | package limiter |
slide_window.lua
1 | local min = now - window |
Nginx
基本概念
Nginx 是目前最流行的 Web 服务器,最初由一位俄罗斯程序员 Igor Sysoev 开发。2019 年,Nginx 被美国的 F5 公司以 6.7 亿美元收购。
- Nginx 的开源版本主要分为两种:
- 主线版 (mainline):最新版本,包含较多新功能和正在开发的实验性模块功能,可能存在一些新的 bug。
- 稳定版 (stable):经过长时间测试,bug 较少,功能较为稳定。
- 安装方式:
- 源码编译安装:拉取源代码后自己编译为可执行文件。
- 预编译二进制包:直接下载编译好的可执行文件。
- Docker Compose:拉取镜像后运行在容器中。
- 主要用途:
- 正向/反向代理:为客户端发出请求或为服务器接收请求。
- 负载均衡:将请求分发到多个操作单元上执行。
- HTTP 服务器:Nginx 也可以作为静态资源服务器使用。
配置文件
Nginx 的主要配置文件通常是 nginx.conf
,并且一般位于 /etc/nginx/
目录下。使用 nginx -t
命令可以测试配置文件的有效性而不需要实际重启 Nginx 服务。
1 | # Nginx 配置文件 |
全局块
全局块是配置文件的第一个块,也是配置文件的主体部分。它主要用来设置一些影响 Nginx 服务器整体运行的配置指令,包括但不限于配置运行 Nginx 服务器的用户(组)、允许生成的 worker process 数量、进程 PID 存放路径、日志存放路径和类型以及配置文件的引入等。
1 | # 指定运行 Nginx 服务器的用户,只能在全局块配置 |
events 块
1 | events { |
http 块
http 块是配置文件的主要部分,包括 http 全局块和 server 块
1 | http { |
server 块
server 块是配置虚拟主机的,⼀个 http 块可以包含多个 server 块,每个 server 块就是⼀个虚拟主机
1 | server { |
常用命令
1 | nginx # 启动 Nginx |
常用模块
1 | # nginx -V |
模块名(Module Name) | 描述(Description) |
---|---|
http_access_module | 接受或拒绝特定的客户端请求 |
http_auth_request_module | 根据子请求的结果实现客户端授权 |
http_auth_basic_module | 使用用户名和密码进行 HTTP 基本认证,限制对资源的访问 |
http_autoindex_module | 自动生成目录列表 |
http_browser_module | 从 User-Agent 请求头中识别客户端浏览器 |
http_charset_module | 为 Content-Type 响应头添加特定字符集 |
http_empty_gif_module | 返回一个 1 像素的透明 GIF 图片 |
http_fastcgi_module | 提供 FastCGI 支持 |
http_geo_module | 根据 IP 地址获取地理位置信息 |
http_gzip_module | 支持 Gzip 压缩 |
http_limit_conn_module | 限制并发连接数 |
http_limit_req_module | 限制请求速率 |
http_map_module | 基于变量映射获取值 |
http_memcached_module | 提供 Memcached 支持 |
http_proxy_module | 提供反向代理支持 |
http_referer_module | 防止盗链 |
http_rewrite_module | 支持 URL 重写 |
http_scgi_module | 将请求转发到 SCGI 服务器 |
http_ssi_module | 处理和支持 SSI(服务器端包含) |
http_split_clients_module | 根据客户端 IP 地址或其他变量将客户端分组,通常用于 A/B 测试 |
http_upstream_hash_module | 提供一致性哈希负载均衡 |
http_upstream_ip_hash_module | 提供 IP 哈希负载均衡 |
http_upstream_keepalive_module | 支持长连接负载均衡 |
http_upstream_least_conn_module | 提供最少连接负载均衡 |
http_upstream_zone_module | 提供共享内存负载均衡 |
http_userid_module | 为客户端设置唯一的 ID(UID、cookie) |
http_uwsgi_module | 将请求转发到 uWSGI 服务器,通常用于 Python 应用 |
统一鉴权
异构的后端服务,使用 API 网关统一鉴权
- 客户端请求: 客户端请求 API 网关,附带 JWT
- API网关鉴权: API 网关验证JWT的有效性和权限(扩展做限流)
- 转发请求: 如果 JWT 有效,API 网关将请求转发给相应的后端服务(还可以添加一些头部信息)
- 后端服务响应: 后端服务处理请求并返回响应,API 网关将响应返回给客户端

通过将 NGINX 作为反向代理和 API 网关,可以在 NGINX 层面集中处理所有的身份验证和授权逻辑,如果验证通过,Nginx 将请求转发到相应的后端服务;如果验证失败,返回 401 未授权错误。将鉴权逻辑外包给专门的鉴权服务,方便管理和扩展。
鉴权服务地址:http://127.0.0.1:7777/api/auth (部署在本地的 Docker 容器)
后端应用服务:http://127.0.0.1:8888 (直接本地运行的后端应用)
转发和鉴权配置:
1 | server { |

鉴权服务
JWT 校验
用户服务做 JWT 的签发与注销(需要依赖第三方组件做记录,如 Redis)
请求不带 JWT 或 JWT 校验失败:
1 | $hcjjj: ~ ❯ curl -i http://127.0.0.1:7777/api/auth |
请求带正确的 JWT :
1 | $hcjjj: ~ ❯ curl -H " Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1IiwidHlwZSI6IkFDQ0VTU19UT0tFTiIsInJvbGUiOiJVU0VSIiwiZXhwIjoxNzI1Njc3NjEzLCJpYXQiOjE3MjA0OTM2MTN9.Z0CXV0B-7USnB6VhHRT4RsN3lxv11R_5h7wxvGoeoLQ " -i http://127.0.0.1:7777/api/auth |
通过鉴权的请求会在其头部添加 user_id、user_role、user_uuid 字段,逻辑为:
鉴权服务 ➡️ Nginx
1 | if permission { |
Nginx ➡️ 对应服务
1 | # 需要鉴权的请求 |
JWT退出机制:使用 Redis 记录已退出的 JWT,每次校验的时候检查一下 JWT 是否在黑名单中,过期时间与 JWT 保存一致
请求限流
[基于 IP 的限流检查] ➡️ [JWT 登录校验] ➡️ [RBAC 权限检查] ➡️ [写入相关信息到请求头并转发至对于后端服务]
1 | // 根据 IP 做限流检查 |
RBAC 鉴权
Nginx 配置
1 | location = /auth-internal { |
- 获取请求的鉴权信息
- 根据 [用户ID,原始 URL,Method] 进行接口权限的检查
- 白名单模式,需要对系统接口进行梳理和配置(后续可扩展为正则表达式或通配符模式)
Ingress

Ingress 是对集群中服务的外部访问进行管理的 API 对象,Ingress可以提供统一的入口控制、HTTP/HTTPS路由、SSL终止和负载均衡等功能。如通过 Ingress 资源来配置不同的转发规则,从而达到根据不同的规则设置访问集群内不同的 Service 所对应的后端 Pod。
API对象(Application Programming Interface Object)通常指的是在软件系统中定义的一组接口和协议,它们允许不同的软件应用程序之间进行交互。API对象可以是数据结构、函数、类或服务,它们提供了一种标准化的方法来访问一个应用程序或服务的功能或数据。
Nginx Ingress Controller 是 Kubernetes 生态系统中广泛使用的 Ingress 控制器之一,上文统一鉴权是用 Nginx 的 auth_request 模块,部署在 K8s 的后需要使用 Nginx Ingress Controller 的 auth-url 注解( External Authentication )或插入自定义 Nginx 配置(Configuration snippet)来实现。

Nginx Ingress
- 安装 heml
- 安装 Kubectl
kubectl
连接集群helm
安装 ingress-nginx- 版本与升级 Supported Versions table
- 新建 Ingress
Ingress
的annotations
主要用于配置全局的行为和特性,如超时、重写规则等,但不能直接修改或插入复杂的location
配置- 使用
nginx.ingress.kubernetes.io/server-snippet
可以添加全局的自定义location
块,但不能针对单个路径 - 使用
nginx.ingress.kubernetes.io/location-snippet
可以针对特定路径添加自定义的location
配置

相关扩展
Kubernetes 资源对象
- 定义: Kubernetes 资源对象是 Kubernetes 集群中用于描述和管理各种资源的 API 对象。它们定义了集群中需要运行和管理的服务、应用程序、网络、存储等。
- 种类: 包括多种类型,如
Pod
、Service
、Deployment
、Ingress
、ConfigMap
、Secret
、PersistentVolume
等。 - 功能: 它们用于声明和管理集群中的资源状态。例如,
Deployment
资源用于管理应用的副本和滚动更新,Service
资源用于定义服务的访问方式。 - 运行方式: 资源对象本身不直接运行在 Pod 中,它们是 Kubernetes 集群的配置和管理单位。它们通过 Kubernetes 控制平面(控制器)和调度器来管理集群中的实际工作负载。
Pod
- 定义:
Pod
是 Kubernetes 中最基本的部署单元,是运行在集群节点上的一个或多个容器的集合。Pod 提供了容器运行时所需的网络和存储资源。 - 功能: Pod 是实际运行应用程序代码的地方。每个 Pod 包含一个或多个容器,这些容器共享网络和存储。Pod 还可以包括 Init 容器、存储卷等。
- 运行方式: Pod 是 Kubernetes 集群中的实际工作负载,它们被 Kubernetes 调度器分配到节点上,并由容器运行时(如 Docker、containerd)执行容器。Pod 通过 Deployment、DaemonSet 或 StatefulSet 等控制器进行管理和调度。
关系
- 资源对象 vs Pod:
Pod
是一种 Kubernetes 资源对象(kind: Pod
),它用于定义和运行容器。其他资源对象(如Deployment
)用于管理 Pod 的生命周期。资源对象提供了集群的配置和管理功能,而 Pod 是实际运行应用程序的实体。 - 管理: Kubernetes 控制平面和调度器使用资源对象来管理和调度 Pod。通过创建和更新资源对象,用户可以控制 Pod 的部署、扩展、更新等操作。
Ingress 资源
定义:
Ingress
是 Kubernetes 的一个标准 API 资源,用于管理集群外部访问服务的路由规则。功能: 它定义了如何将 HTTP 和 HTTPS 请求路由到集群内部的服务。通过
Ingress
资源,可以指定路径、主机、证书等信息。实现:
Ingress
可以由不同的控制器来实现,包括 Nginx、Traefik、HAProxy、Istio 等。具体的功能和配置会依赖于你使用的Ingress
控制器。运行:
Ingress
本身不是一个运行中的组件,它只是一个 Kubernetes 资源对象。它定义了路由规则、主机、路径等,控制如何将请求转发到服务。部署: 你通过
kubectl apply
命令创建或更新Ingress
资源,它会被 Kubernetes 控制平面管理,并与相应的 Ingress 控制器一起工作。
NginxIngress 控制器
- 定义:
NginxIngress
控制器(即Ingress-Nginx
控制器)是实现Ingress
资源的具体方案之一。 - 功能: 它使用 Nginx 作为反向代理服务器,将外部请求根据
Ingress
规则转发到集群内部的服务。它提供了许多高级功能,如自定义 Nginx 配置、TLS 终止、路径重写等。 - 配置:
Ingress-Nginx
控制器通常通过ConfigMap
、Ingress
注解等方式进行配置。它还允许使用注解来修改或扩展 Nginx 的行为。 - 运行:
NginxIngress
控制器运行在 Kubernetes 集群中的 Pod 中。它通常是一个或多个 Pod 的集合,这些 Pod 运行着 Nginx 实例,负责根据Ingress
资源的规则处理和路由流量。 - 部署:
Ingress-Nginx
控制器作为 Kubernetes 的 Deployment 或 DaemonSet 部署,通常会在kube-system
命名空间中或其他指定的命名空间中运行。
关系
- Ingress Controller 是一个运行在 Kubernetes 集群中的应用程序,它负责实现 Ingress 资源定义的规则。它监听 Ingress 资源的变化,并根据这些变化来配置自己的负载均衡和服务路由规则。你提供的 Service 配置是 NGINX Ingress Controller 的一部分,它作为服务运行在集群中,并被 Kubernetes API 管理。
- Ingress 资源 是 Kubernetes 的 API 对象,它定义了如何将外部请求路由到集群内的服务。Ingress 资源包含了路由规则,例如基于域名或路径的路由。
- 首先,你需要部署一个 Ingress Controller(比如 NGINX Ingress Controller)到你的 Kubernetes 集群中。这个 Controller 会作为一个服务运行,并且监听 API Server 以获取 Ingress 资源的变化。
- 然后,你创建 Ingress 资源,定义了路由规则。这些规则告诉 Ingress Controller 如何将进入的请求转发到集群内的特定服务。
- 当外部请求到达时,它们首先会被 Ingress Controller 接收,然后根据 Ingress 资源中定义的规则,将请求路由到正确的服务。
- Ingress Controller 是实现 Ingress 规则的组件,而 Ingress 资源定义了这些规则
问题记录
如何查看 Ingress 配置是否加载到 Nginx 中
Pod 实例名称:ingress-web-ingress-nginx-controller-5cxxxxxf4b-jxxxr
1 | ingress-web-ingress-nginx-controller-5cxxxxxf4b-jxxxr:/etc/nginx$ pwd |
测试 ingress-nginx-controller
访问鉴权服务的接口
1 | ingress-web-ingress-nginx-controller-5cxxxxxf4b-jxxxr:/etc/nginx$ curl -H "Authorization:bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1IiwidHlwZSI6IkFDQ0VTU19UT0tFTiIsInJvbGUiOiJVU0VSIiwiZXhwIjoxNzM0NTc1MDQzLCJpYXQiOjE3MjkzOTEwNDN9.4WvI5ldUidYxtYonCNZZ2rurW6U2B8A5xSpLhs-_WUU" -i http://auth-service.default:7777/api/auth |
持续查看 Nginx 日志
1 | ingress-web-ingress-nginx-controller-54xxxxxx58-bh4bl:/etc/nginx$ tail -f /var/log/nginx/nginx_access.log |