【面试突击】Spring Security + OAuth2 密码模式实战:Gateway 作为网关与资源服务器,Auth 作为认证服务器(完整认证链路解析)

文章目录

    • [1. 架构与职责划分](#1. 架构与职责划分)
      • [1.1 Auth 服务(认证服务器)](#1.1 Auth 服务(认证服务器))
      • [1.2 Gateway(网关 + 资源服务器)](#1.2 Gateway(网关 + 资源服务器))
    • [2. 登录请求形态(Password 模式)](#2. 登录请求形态(Password 模式))
    • [3. Auth 服务内部:从 Basic 校验到签发 JWT 的完整链路](#3. Auth 服务内部:从 Basic 校验到签发 JWT 的完整链路)
      • [3.1 Basic 校验:进入 `BasicAuthenticationFilter`(发生在 Auth)](#3.1 Basic 校验:进入 BasicAuthenticationFilter(发生在 Auth))
      • [3.2 进入 Token 端点:`/oauth/token` 封装用户登录凭证](#3.2 进入 Token 端点:/oauth/token 封装用户登录凭证)
      • [3.3 `ProviderManager` 循环调用 `AuthenticationProvider`,命中自定义 Provider](#3.3 ProviderManager 循环调用 AuthenticationProvider,命中自定义 Provider)
      • [3.4 调用 `UserDetailsService#loadUserByUsername` 加载用户信息](#loadUserByUsername` 加载用户信息)
      • [3.5 签发 Token:Access Token 为 JWT,同时写入 Redis](#3.5 签发 Token:Access Token 为 JWT,同时写入 Redis)
    • [4. 前端:缓存 Token 并在后续请求携带](#4. 前端:缓存 Token 并在后续请求携带)
    • [5. 资源访问链路:Gateway 远程校验(用于吊销)→ 放行并分发 → 透传用户信息](#5. 资源访问链路:Gateway 远程校验(用于吊销)→ 放行并分发 → 透传用户信息)
      • [5.1 Gateway:扩展 `RemoteTokenServices` 调用 `/oauth/check_token`](#5.1 Gateway:扩展 RemoteTokenServices 调用 /oauth/check_token)
      • [5.2 Gateway:解析用户信息并写入请求头透传给下游](#5.2 Gateway:解析用户信息并写入请求头透传给下游)
    • [6. 全链路总结(按执行顺序)](#6. 全链路总结(按执行顺序))

本文分享一套在微服务体系中落地认证与鉴权的实现方式:使用 Spring Security + OAuth2(Password密码模式) ,由 Auth 服务 作为认证服务器(Authorization Server),由
Gateway 同时承担 网关资源服务器 (Resource Server)的职责。核心目标是把链路讲清楚:从「前端登录」到「签发 JWT 并写入 Redis」再到「网关校验
Token、透传用户信息」的完整流程,并把 关键过滤器ProviderUserDetailsServiceTokenStore
的调用顺序按实际执行顺序梳理出来,便于理解与排查问题。


适用范围说明(很重要)

本文的实现方式基于 Password Grant(密码模式) 。该模式在 OAuth2.1

中已不再推荐,但在企业内部系统里,如果属于自家第一方应用(前端与后端同属一个安全域、可控且强信任),仍然有较多落地案例。本文聚焦工程实现与链路剖析,不讨论协议演进取舍。


1. 架构与职责划分

1.1 Auth 服务(认证服务器)

Auth 服务提供两类能力:

  • 签发 Token
    • POST /oauth/token:密码模式换取 Token
  • 校验 Token
    • POST /oauth/check_token:供资源服务器远程校验使用(Token Introspection)

在 Auth 服务中会做一些定制化扩展(不同团队会略有差异):

  • 自定义 Client 认证 Provider (例如 ClientAuthenticationProvider):用于 Basic 方式校验 clientId/clientSecret
  • 自定义 UserDetailsService#loadUserByUsername:加载用户信息
  • 自定义 TokenStore:将 Token 数据写入 Redis(用于会话管理、吊销、风控扩展等)

关键点:Basic 认证发生在 Auth 服务。Gateway 只负责转发登录请求,不承担 client 认证。

1.2 Gateway(网关 + 资源服务器)

Gateway 主要承担三件事:

  • 统一入口与路由转发
    • 登录请求:前端先请求 Gateway,由 Gateway 转发到 Auth 的 /oauth/token
  • 资源服务器鉴权
    • 业务请求携带 Token 到达 Gateway 后,Gateway 先校验 Token,再决定是否分发
  • 用户上下文透传
    • 在 Gateway 内通过过滤器/拦截器从 JWT(或校验结果)中提取用户 ID 等信息,写入请求头透传给下游微服务

2. 登录请求形态(Password 模式)

登录时,前端会向 Gateway 发起请求(随后由 Gateway 转发到 Auth),典型请求如下:

  • Header:

    • Authorization: Basic base64(clientId:clientSecret)
    • Content-Type: application/x-www-form-urlencoded
  • Body(表单):

    • grant_type=password
    • username=xxx
    • password=yyy
    • (可选)scope=all

3. Auth 服务内部:从 Basic 校验到签发 JWT 的完整链路

下面按实际执行顺序梳理 Auth 服务内发生的事情。

3.1 Basic 校验:进入 BasicAuthenticationFilter(发生在 Auth)

请求头带了:

复制代码
Authorization: Basic base64(clientId:clientSecret)

因此会先经过 BasicAuthenticationFilter。该过滤器解析 Basic 头后,会把 OAuth2 Client 的认证请求交给 Spring Security 的认证体系处理。

这一阶段的目标是:认证 OAuth2 Client 是否合法(不是认证业务用户)。

此处通过自定义的 ClientAuthenticationProvider 完成 clientId/clientSecret 校验(可对接数据库、缓存或配置中心)。

整体过程可以概括为:

  1. BasicAuthenticationFilter 解析 Basic Header
  2. 构造 Authentication 请求对象(具体类型取决于实现)
  3. 调用 AuthenticationManager.authenticate(...) 完成 Client 认证

3.2 进入 Token 端点:/oauth/token 封装用户登录凭证

当 Client 认证通过后,请求进入 OAuth2 的 Token Endpoint:/oauth/token

当请求参数为 grant_type=password 时,框架会读取 usernamepassword,并封装为:

  • UsernamePasswordAuthenticationToken

随后调用:

  • AuthenticationManager.authenticate(usernamePasswordAuthenticationToken)

默认的 AuthenticationManager 实现通常是:

  • ProviderManager

3.3 ProviderManager 循环调用 AuthenticationProvider,命中自定义 Provider

ProviderManager 内部维护一组 AuthenticationProvider,会按顺序循环执行:

  • supports(authentication.getClass())
  • 若支持则调用 authenticate(authentication)

最终会命中自定义的用户认证 Provider(或基于 DaoAuthenticationProvider 扩展的实现),进入用户认证逻辑。


3.4 调用 UserDetailsService#loadUserByUsername 加载用户信息

在自定义 Provider 中会调用:

  • UserDetailsService#loadUserByUsername(username)

由该方法完成用户信息加载(查库/缓存/远程服务均可),返回 UserDetails。随后结合 PasswordEncoder 完成密码比对,并检查账户状态(是否锁定、是否过期、是否启用等)。

认证通过后,会生成一个 authenticated=trueAuthentication,其中包含用户主体信息与权限集合(GrantedAuthorities)。


3.5 签发 Token:Access Token 为 JWT,同时写入 Redis

用户认证成功后,OAuth2 进入 Token 签发流程:

  • 生成 OAuth2AccessToken(以及可选的 Refresh Token)
  • 通过自定义 TokenStore 写入 Redis

这里的关键约定是:

  • 返回给前端的 access_token 是 JWT,其中会包含部分用户字段(例如用户 ID、用户名等 claims)
  • Redis 扮演"服务端会话/状态"的角色 :用于存储"全量 token 或 token 索引信息",以支持后续的主动吊销、强制下线、黑名单等能力

典型响应结构如下:

json 复制代码
{
  "access_token": "xxx.yyy.zzz",
  "token_type": "bearer",
  "refresh_token": "yyy",
  "expires_in": 43199,
  "scope": "all"
}

4. 前端:缓存 Token 并在后续请求携带

前端拿到 access_token 后,会写入 sessionStorage(或 localStorage),后续访问业务接口时统一携带:

复制代码
Authorization: Bearer <access_token>

5. 资源访问链路:Gateway 远程校验(用于吊销)→ 放行并分发 → 透传用户信息

5.1 Gateway:扩展 RemoteTokenServices 调用 /oauth/check_token

业务请求到达 Gateway 后,Gateway 作为资源服务器需要先校验 Token。

这里采用 远程校验(Token Introspection)

  • 在 Gateway 中扩展/重写 RemoteTokenServices
  • 调用 Auth 的 POST /oauth/check_token 校验 token 是否有效

之所以在 access_token 已经是 JWT 的情况下仍然保留 /check_token,核心目的是引入服务端可控性

即使 JWT 在结构上仍然"可验签且未过期",也可以通过 Redis/服务端状态实现主动吊销(例如注销、踢下线、密码修改后使旧 token 失效等)。

注意:/oauth/check_token 通常也需要 client 认证(Basic)。因此 Gateway 调用该接口时一般也需要携带 client 的 Basic 认证信息,避免把 introspection 接口暴露为匿名可用。


5.2 Gateway:解析用户信息并写入请求头透传给下游

Gateway 内会实现拦截器/过滤器,用于:

  • 从 JWT(或 /check_token 返回的 claims)中提取:

    • 用户 ID
    • 用户名
    • 租户 ID
    • 角色/权限等
  • 写入请求头透传到下游微服务,例如:

    X-User-Id: 10001
    X-Username: sfsfsfsdf
    X-Tenant-Id: t001

同时会做一个重要约束:网关负责清洗并覆盖这些内部请求头 ,避免客户端伪造 X-User-Id 等字段;下游服务只信任来自网关的内网请求。


6. 全链路总结(按执行顺序)

  • 登录链路

    前端请求 Gateway → Gateway 转发 Auth

    → Auth:BasicAuthenticationFilter 校验 Client(自定义 ClientAuthenticationProvider

    → 进入 /oauth/token

    → 封装 UsernamePasswordAuthenticationToken

    AuthenticationManager.authenticate(...)ProviderManager

    → 命中自定义用户 Provider

    UserDetailsService#loadUserByUsername

    → 签发 JWT(自定义 TokenStore 写入 Redis)

    → 返回 Token 给前端

  • 访问链路

    前端携带 Authorization: Bearer <token> 请求 Gateway

    → Gateway:RemoteTokenServices 调 Auth /oauth/check_token 远程校验(用于支持吊销/强制失效)

    → 校验通过后放行

    → Gateway 提取用户信息写入请求头

    → 分发到下游微服务

相关推荐
程序猿零零漆2 小时前
Spring之旅 - 记录学习 Spring 框架的过程和经验(三)Bean的依赖注入配置、Spring的其它配置标签
java·学习·spring
Marshmallowc2 小时前
CSS 布局原理:为何“负边距”是栅格系统的基石?
前端·css·面试
是娇娇公主~2 小时前
HTTPS 常用密钥交换算法解析
网络协议·http·面试·https
程序猿零零漆2 小时前
Spring之旅 - 记录学习 Spring 框架的过程和经验(二)基于Xml方式Bean的配置
xml·spring
L1624763 小时前
Redis 删除缓存全场景操作手册(详细版)
redis·spring·缓存
廋到被风吹走12 小时前
【Spring】DispatcherServlet解析
java·后端·spring
廋到被风吹走13 小时前
【Spring】PlatformTransactionManager详解
java·spring·wpf
wanghowie13 小时前
01.07 Java基础篇|函数式编程与语言新特性总览
java·开发语言·面试
それども14 小时前
Spring Bean 的name可以相同吗
java·后端·spring