当前位置: 首页 > news >正文

IdentityServer4认证授权之授权码模式(Authorization Code+PKCE)

前言

1.配置服务认证授权

订单服务(Ordering.API)配置了认证授权,所以我们使用Scalar调试接口的时候也需要认证服务(Identity.API)颁发的token,上面的测试就是401无权限

首先我们再回忆下我们认证授权配置了那些:

  • 认证服务(Authority):我们本地启动的Identity.API,其他服务认证授权配置Identity.API的地址即可。
  • 使用Https协议获取认证中心(Authority )元数据(RequireHttpsMetadata):原来这里是false,访问不验证https,为了多学习我们设置为true,本身Identity.API启动就是使用的https协议
  • 订阅人(Audience):确保认证服务Identity.API颁发的token是发给我的(订单服务),就是我们在appsettings.json中配置"Audience": "orders"。简单理解:什么服务就配置什么服务的标识,但是认证服务同时也要有此标识。确保这个Token 是发给当前 API 的,而不是其他服务。验证签发tokenaud
  • 签发者(Issuers):确保Token是由受信任的认证中心签发。验证签发tokeniss

image-20251104012258084

还需要再回忆一下认证服务(Identity.API)中的客户端配置,找到orderingswaggerui配置

image-20251104014215886

然后我们启动认证服务,在..\eShop\src\Identity.API目录下运行以下命令:

dotnet run --urls https://localhost:5243/

image-20251103193457394

2.配置授权码模式(Authorization Code + PKCE)

隐式模式(Implicit)已经被淘汰了,eShop 官方示例用 Implicit,是为了演示方便(快速看到授权效果),让 Swagger UI(或浏览器)/Scalar能够直接拿到 Access Token,方便开发和测试。

在 OAuth 2.1 规范中(2023 年正式发布),Implicit 模式已被标记为弃用

  • Access Token 暴露在 URL;
  • 无法安全刷新;
  • 浏览器中易被脚本窃取;
  • 无法防止中间人攻击。

所以现代标准中 强制推荐改用 Authorization Code + PKCE

既然学习,我们也看看授权码模式(Authorization Code + PKCE),我们就以购物车服务(Basket.API)为例,因为购物车的Scalar也是我们为了学习加上的,购物车服务(Basket.API)因为使用了Grpc,所以只能使用https协议,我们把接口也加上了api版本

image-20251104235344290

先添加以下配置:

 // 授权类型:Authorization Code + PKCEAllowedGrantTypes = GrantTypes.Code,// SPA/Scalar 客户端不保存 secret,所以不要求 client_secretRequireClientSecret = false,// 强制使用 PKCE(安全性必须)RequirePkce = true,// 是否显示用户同意授权页面RequireConsent = true, // 强制每次授权确认// token 生命周期和刷新控制(可选)AllowOfflineAccess = false, // 如果暂时不需要 refresh token// 允许跨域请求的 originAllowedCorsOrigins ={"https://localhost:5221"  // Scalar / Swagger UI 所在的域名},

为什么授权码模式(Authorization Code + PKCE)会触发跨域?隐式模式(Implicit )不需要(这是我调试过程遇到的问题,我提前记录一下)

Implicit 流 下:

  • 浏览器通过 302 重定向 + URL fragment 获取 token
  • 不走 JS Ajax,所以不会触发 CORS

Authorization Code + PKCE 下:

  • 前端 SPA 需要用 JS 通过 POST /connect/token 来交换 code → access_token
  • 浏览器 JS 发起的是跨域 AJAX 请求
  • IdentityServer 如果没有允许该 Origin,就报 CorsPolicyService did not allow origin: ...

image-20251105000220581

然后配置回调地址(RedirectUris)和允许访问的的API范围(AllowedScopes)

为什么需要配置 RedirectUris

  • OAuth2 / OIDC 协议要求每个客户端注册 回调地址(redirect_uri)。

  • 当用户在 IdentityServer 授权成功后,浏览器会重定向到这个地址,并带上 code 或 token

  • 严格匹配:IdentityServer 会检查请求中的 redirect_uri 是否在客户端注册列表里,否则会报:Invalid redirect_uri

为什么还要配置 openid / profile scopes?

这些 scope 来自 OpenID Connect (OIDC) 协议:

  • openid → 表示这是一个 OIDC 请求(IdentityServer 会返回 id_token)
  • profile → 请求用户信息(如名字、头像等)

区别于普通 API scope

  • API scope(比如 basket)只控制访问后端 API
  • openid/profile 用于获取用户身份信息(JWT 中的 claims)

如果你的前端只需要访问 API,不关心用户信息,可以不用 openid/profileopenid/profile 可以根据需要决定是否保留。

image-20251105000716645

定义一个名为 "basket" 的受保护 API,并指定访问它需要 "basket" 这个 scope。

image-20251105001310689

OAuth2新增授权码模式(Authorization Code + PKCE )

image-20251105001538818

RequireAuthorization给这个 API 分组下的所有端点应用授权策略,也就是需要用户已经登录或具备某些权限才能访问这些接口。

var api = vApi.MapGroup("api/Basket").HasApiVersion(1, 0).HasApiVersion(2, 0).RequireAuthorization();

image-20251105003204676

如果配置RequireAuthorization()没有token访问接口返回401(无权限)

image-20251105003342117

所有代码配置完成后,启动购物车服务(Basket.API),打开Scalar,选择OAuth2,发现多了一种授权方案(Authorization Code)

跟之前一样配置:Client IDUse PKCEScopes

Client Secret 可选

  • 对于 SPA 或前端客户端(比如 Scalar 文档 UI、Swagger UI),不存储密钥是推荐做法,因为前端代码无法安全保管 secret。

  • 所以在IdentityServer中,你可以配置 RequireClientSecret = false

  • 如果你在 Scalar 上看到 Client Secret: XYZ123,那只是生成的示例,并不是必须的。

PKCE 强制使用 SHA-256

  • PKCE(Proof Key for Code Exchange)是 Authorization Code 流的增强安全机制,用于前端应用防止授权码被截获。
  • PKCE 流程里有一个 code_challenge,必须经过 SHA-256 哈希(S256)生成,原始的 plain 方法被现代安全标准弃用。
  • 因此 IdentityServer 会要求 code_challenge_method = S256,不能用 plain,否则报错:code_challenge_method of plain is not allowed

image-20251105001926111

输入用户名和密码,然后点击登录

image-20251105002848800

再点击Yes,Allow(允许授权)

image-20251105002929930

打开/api/Basket/{customerId},测试一下,查询正常

image-20251105003038439

3.授权码模式流程(Authorization Code + PKCE)

在查看 OpenAPI 规范文档时,我们为 oauth2 安全方案配置了两种授权模式:ImplicitAuthorization Code,分别对应浏览器直接获取 token 和通过授权码交换 token 的安全流程,并指定了 basket API 的访问范围。

https://localhost:5221/openapi/v1.json

image-20251105004034522

先关闭认证服务(Identity.API),然后设置Client IDUse PKCEScopes,点击Authorize

查看Scalar发起的授权请求:

  • response_type=code:授权码模式。
  • scope:请求访问的范围。
  • code_challenge & code_challenge_method:PKCE,客户端生成随机字符串和散列,服务器用来校验。
  • redirect_uri:授权成功后回调地址。
  • state:防 CSRF,同时保持请求上下文
  • client_id:当前客户端的唯一标识
# 原请求
https://localhost:5243/connect/authorize?response_type=code&code_challenge=3_V716AyAbS7uKOkhK6jDTW7Ho8As2_zq6EOBA_i7jQ&code_challenge_method=S256&redirect_uri=https%3A%2F%2Flocalhost%3A5221%2Fscalar%2Fv1&client_id=basketswaggerui&state=78jzgb2i&scope=basket
# 解码后 redirect_uri加密了
https://localhost:5243/connect/authorize?
response_type=code&
code_challenge=3_V716AyAbS7uKOkhK6jDTW7Ho8As2_zq6EOBA_i7jQ&
code_challenge_method=S256&
redirect_uri=https://localhost:5221/scalar/v1&
client_id=basketswaggerui&
state=78jzgb2i&
scope=basket

image-20251105004502293

启动认证服务(Identity.API)和购物车服务(Basket.API),设置Client IDUse PKCEScopes,点击Authorize

没登录前:Showing login: User is not authenticated(显示登录页,用户没有认证)

image-20251105005336634

登陆成功后:Showing consent: User has not yet consented(显示授权:用户未授权)

image-20251105005723209

登录&授权后:Scalar发送 POST 请求到 Token 端点

  • client_id:客户端唯一标识
  • redirect_uri:回调地址,必须匹配注册信息
  • code:授权码,用于换取 Access Token
  • grant_type:授权模式,这里是授权码模式(authorization_code)
  • code_verifier:PKCE 验证码,保证授权码安全
POST https://localhost:5243/connect/token
client_id=basketswaggerui&
redirect_uri=https%3A%2F%2Flocalhost%3A5221%2Fscalar%2Fv1&
code=32BAD9BF9537F37C142CAC44066BFC80F5A81DDBB61B74E06445FA61881D0A85-1&
grant_type=authorization_code&
code_verifier=5JddRWZjBe72ixVPwZMxIQblPyhe_MivmcNUQFa2LEI

为什么授权码模式(Authorization Code + PKCE)会触发跨域?隐式模式(Implicit )不需要(这是我调试过程遇到的问题,我提前记录一下)

上面记录了这个问题,就是因为Scalar客户端发起了Ajax请求获取token,所以认证服务(Identity.API)需要配置AllowedCorsOrigins

image-20251105005842494Scalar设置Access Token,所有api调用就可以使用这个Access Token

image-20251105010600233

SPA/Scalar 通常不使用 Refresh Token,是因为浏览器无法安全存储长期凭证,短期 Access Token + PKCE 重新授权比使用 Refresh Token 更安全。

image-20251105010745695

流程说明

步骤 Authorization Code + PKCE Flow
1 用户点击 Authorize
2 跳转 IdentityServer 登录
3 如果未登录 → 输入用户名/密码
4 用户同意授权
5 IdentityServer 重定向回客户端(带 授权码 code
6 客户端使用 code + code_verifier 向 IdentityServer Token Endpoint 交换 Access Token
7 IdentityServer 返回 Access Token(可选 ID Token)
8 客户端使用 Access Token 调用受保护 API

📌 创作不易,感谢支持!

每一篇内容都凝聚了心血与热情,如果我的内容对您有帮助,欢迎请我喝杯咖啡☕,您的支持是我持续分享的最大动力!

💬 加入交流群(QQ群):576434538

微信打赏

http://www.jsqmd.com/news/60070/

相关文章:

  • IdentityServer4认证授权之OpenId Connect认证流程
  • IdentityServer4认证授权之OpenId Connect方案
  • IdentityServer4认证授权之隐式流模式(Implicit)
  • .NET Core 微服务之RabbitMQ分布式链路追踪
  • Asp.Net Core 过滤器之Filter
  • Asp.Net Core 请求管道中间件之Middleware
  • Git推送从失败到成功的解决方案
  • Microsoft-Store-error
  • STM32 HAL库 硬件IIC 从机一些问题整理(转载)
  • 本地人推荐的火锅,天台火锅/麻辣火锅/老火锅/市井火锅/川渝火锅约会地点推荐榜单
  • OBDSTAR P003+ Kit for DC706 Tablets: Simplify ECU, EEPROM, Flash IMMO Data Diagnostics Programming
  • 全程复盘:一次枚举值永远 Cloud2的坑——从玄学随机到只读属性
  • M726芯片
  • Fast Easy Electric Oil Siphon Pump: Professional Fluid Transfer for Cars, Motorcycles Boats
  • AutoCloseable接口 try-with-resources 、 try-catch-finally
  • 第44天(中等题 数据结构)
  • rizhi
  • element-plus el-select
  • centos6.9编译安装python37——SSL 模块缺失、GCOV 链接错误,以及 Bash 命令缓存混乱
  • 在 Windows 上本地部署 ComfyUI + zImage Turbo 模型(低显存友好)
  • sg.取消按钮焦点框
  • 代码随想录Day27_贪心1
  • Day10-20251203
  • 面向人机文明的价值协同:理论、实践与评估的完整框架
  • 251203 完成比完美重要
  • python调用大模型api来进行对话
  • 主流玩家的高端主板!七彩虹战斧B850M超级黑刃主板评测:供电散热配置豪华 性价比极佳
  • 6.4 基于线弹性断裂力学(LEFM)的断裂参数
  • expdp dmp 导出不完整导入ORA-39059 ORA-39246 故障抢救数据
  • 基于 Node.js 与 Tesseract.js 的验证码识别系统设计与实现