网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
folder-controller
folder-middleware
folder-class
folder
folder-secure
folder-controller
folder-views
folder-public
csharp
docker
visualstudio
csharp
readme

SSO 架构图

[用户点击登录] → [B/C/D 应用跳转到 A 的 /connect/authorize]
      ↓
[认证中心 A 检查是否登录]
      ↓
┌───────────────┬────────────────────┐
│ 未登录        │ 已登录             │
│ → Challenge() │ → 构造 Claims      │
│ → 登录页      │ → 检查授权记录     │
│ → 登录成功    │ → 显示授权页/跳过 │
└───────────────┴────────────────────┘
      ↓
[返回授权码 → 客户端换 Token → 登录完成]

每个 Response Type 常量的含义

常量含义对应授权流程
Code只返回授权码授权码流程(推荐)
CodeIdToken授权码 + ID Token混合流程
CodeToken授权码 + Access Token混合流程
CodeIdTokenToken授权码 + ID Token + Access Token混合流程
IdToken只返回 ID Token隐式流程
IdTokenTokenID Token + Access Token隐式流程
Token只返回 Access TokenOAuth 隐式流程(非 OIDC)
None不返回任何 Token特殊用途(设备授权、登录确认等)

Dpz.Core.Auth 说明

本项目基于 OpenIddict 提供 OIDC/OAuth2 能力,结合自定义的 Store 通过 MongoDB 持久化应用、授权、作用域与令牌信息。

运行环境

  • .NET 8+
  • MongoDB(连接字符串配置键:ConnectionStrings:mongodb

依赖注入与存储

在启动处注册仓储与自定义 Store(示例):

services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

services.AddScoped<IOpenIddictApplicationStore<DpzApplication>, DpzApplicationStore>();
services.AddScoped<IOpenIddictAuthorizationStore<DpzAuthorization>, DpzAuthorizationStore>();
services.AddScoped<IOpenIddictScopeStore<DpzScope>, DpzScopeStore>();
services.AddScoped<IOpenIddictTokenStore<DpzToken>, DpzTokenStore>();

services.AddOpenIddict()
    .AddCore(options =>
    {
        // 使用自定义实体与存储
        options.UseQuartz(); // 可选
    })
    .AddServer(options =>
    {
        options.SetTokenEndpointUris("/connect/token");
        options.SetAuthorizationEndpointUris("/connect/authorize");
        options.SetUserinfoEndpointUris("/connect/userinfo");

        options.AllowAuthorizationCodeFlow()
               .AllowClientCredentialsFlow();

        options.AddDevelopmentEncryptionCertificate()
               .AddDevelopmentSigningCertificate();

        options.UseAspNetCore()
               .EnableTokenEndpointPassthrough()
               .EnableAuthorizationEndpointPassthrough();
    });

控制器端点

  • GET /connect/authorize:发起授权码流程,要求登录;构建 Claims 并返回授权结果。
  • POST /connect/token:Exchange 操作,支持客户端凭据模式等。
  • GET /connect/userinfo:返回当前用户的 Claims。

索引与优化建议(MongoDB)

  • DpzScope.Name:唯一索引。
  • DpzToken.ReferenceId:普通或唯一索引;ExpirationDate/RedemptionDate:TTL 索引(可选)。
  • DpzAuthorization.ApplicationId/Subject/Status/TypeDpzToken.ApplicationId/AuthorizationId/Subject/Status/Type:按查询热度建立索引。

注意事项

  • client 查询参数目前视为 ApplicationId(ObjectId)。如需支持 ClientId 字符串,请先查询应用集合获取 ApplicationId 再过滤,或新增专用方法。
  • Properties 采用 BsonDocumentImmutableDictionary<string, JsonElement> 互转,复杂嵌套可按需扩展映射。

快速自测

  1. 注册测试应用(clientId/secret)
  2. POST /connect/token 使用客户端模式获取 token
  3. GET /connect/userinfo 使用 Bearer 调用,验证返回声明

创建自签名证书

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=OpenIddictDev"
openssl pkcs12 -export -out openid-client-dev.pfx -inkey key.pem -in cert.pem -password pass:your-password

# 将 openid-client-dev.pfx 文件内容进行 Base64 编码后存入配置中心:
base64 openid-client-dev.pfx > cert-base64.txt

🔐 机密客户端 vs 公开客户端

特性机密客户端 (Confidential Client)公开客户端 (Public Client)
典型场景后端服务、Web Server、桌面应用SPA (React/Vue/Blazor WASM)、移动 App
是否能安全保存 client_secret✅ 可以(存放在服务器或安全存储)❌ 不行(前端代码/设备随时可被反编译)
OAuth2 授权模式授权码模式 (Authorization Code Flow)
客户端凭证模式 (Client Credentials Flow)
授权码模式 + PKCE (Proof Key for Code Exchange)
刷新 Token需要 client_id + client_secret + refresh_token只需要 client_id + refresh_token(无 secret)
安全假设运行环境可控,secret 不会泄露运行环境不可信,secret 必然会泄露
适用场景企业后端 API、BFF、守护进程、Job 服务浏览器前端、移动端、桌面轻客户端

🔄 刷新 Token 流程对比

机密客户端 (Confidential Client)

用户 → 客户端(后端服务) → 认证中心

  1. 客户端保存 client_idclient_secret`(安全存储在服务器端)。
  2. 客户端发起刷新请求:POST /connect/token grant_type=refresh_token client_id=server_app client_secret=super_secret refresh_token=xxxx
  3. 认证中心验证 client_id + client_secret,确认合法。
  4. 返回新的 access_token(和可选的新的 refresh_token)。
  5. 客户端安全保存新的 Token。

公开客户端 (Public Client, SPA / Blazor WASM)

用户浏览器 → 前端应用 → 认证中心

  1. 前端应用只保存 client_id没有 client_secret
  2. 前端发起刷新请求:POST /connect/token grant_type=refresh_token client_id=spa_app refresh_token=xxxx
  3. 认证中心验证 client_id,确认这是一个 Public Client
  4. 返回新的 access_token(和可选的新的 refresh_token)。
  5. 前端保存新的 Token(通常放在内存或安全存储区,而不是 LocalStorage)。

📌 对比总结

流程环节机密客户端公开客户端
是否需要 client_secret✅ 是❌ 否
Secret 存储位置服务器安全存储不存在(不能放前端)
典型场景后端服务、Job、BFFSPA、Blazor WASM、移动端
安全假设环境可控,secret 不会泄露环境不可信,secret 必然会泄露

docker 部署

# 生产环境
cd /home/ubuntu/project/dpz.core

git pull

cd /home/ubuntu/project/dpz.core/src

sudo docker build -t dpz.core.auth -f Dpz.Core.Auth/Dockerfile .

sudo docker run --restart=always \
--name dpz.core.auth \
-e TZ=Asia/Shanghai \
-p 2377:8080 \
-d dpz.core.auth:latest
loading