网站首页 网站源码

[用户点击登录] → [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 | 隐式流程 |
IdTokenToken | ID Token + Access Token | 隐式流程 |
Token | 只返回 Access Token | OAuth 隐式流程(非 OIDC) |
None | 不返回任何 Token | 特殊用途(设备授权、登录确认等) |
本项目基于 OpenIddict 提供 OIDC/OAuth2 能力,结合自定义的 Store 通过 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。DpzScope.Name:唯一索引。DpzToken.ReferenceId:普通或唯一索引;ExpirationDate/RedemptionDate:TTL 索引(可选)。DpzAuthorization.ApplicationId/Subject/Status/Type、DpzToken.ApplicationId/AuthorizationId/Subject/Status/Type:按查询热度建立索引。client 查询参数目前视为 ApplicationId(ObjectId)。如需支持 ClientId 字符串,请先查询应用集合获取 ApplicationId 再过滤,或新增专用方法。Properties 采用 BsonDocument 与 ImmutableDictionary<string, JsonElement> 互转,复杂嵌套可按需扩展映射。POST /connect/token 使用客户端模式获取 tokenGET /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
| 特性 | 机密客户端 (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 服务 | 浏览器前端、移动端、桌面轻客户端 |
用户 → 客户端(后端服务) → 认证中心
client_id 和 client_secret`(安全存储在服务器端)。client_id + client_secret,确认合法。access_token(和可选的新的 refresh_token)。用户浏览器 → 前端应用 → 认证中心
client_id,没有 client_secret。client_id,确认这是一个 Public Client。access_token(和可选的新的 refresh_token)。| 流程环节 | 机密客户端 | 公开客户端 |
|---|---|---|
是否需要 client_secret | ✅ 是 | ❌ 否 |
| Secret 存储位置 | 服务器安全存储 | 不存在(不能放前端) |
| 典型场景 | 后端服务、Job、BFF | SPA、Blazor WASM、移动端 |
| 安全假设 | 环境可控,secret 不会泄露 | 环境不可信,secret 必然会泄露 |
# 生产环境
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
