1.OpenID Connect相关接口添加到swagger文档

2.添加jwt认证
This commit is contained in:
ShaoHua
2025-12-30 00:37:56 +08:00
parent 93f2eaf285
commit e0ab9c836c
3 changed files with 252 additions and 0 deletions
@@ -40,6 +40,7 @@ using Volo.Abp.OpenIddict;
using Volo.Abp.Swashbuckle;
using Volo.Abp.Studio.Client.AspNetCore;
using Volo.Abp.Security.Claims;
using Hua.Abp.Demo.Swagger;
namespace Hua.Abp.Demo;
@@ -135,6 +136,12 @@ public class DemoHttpApiHostModule : AbpModule
var configuration = context.Services.GetConfiguration();
context.Services.AddAuthentication()
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.Audience = "Demo";
})
.AddOpenIdConnect("WeGit", "Login with WeGit", options =>
{
options.Authority = "https://git.we965.cn";
@@ -241,6 +248,31 @@ public class DemoHttpApiHostModule : AbpModule
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
// 注册 OpenIddict 文档过滤器
options.DocumentFilter<OpenIddictDocumentFilter>();
// 添加 JWT Bearer 认证支持
var securityScheme = new OpenApiSecurityScheme
{
Name = "Authorization",
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
Reference = new OpenApiReference
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
};
options.AddSecurityDefinition("Bearer", securityScheme);
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{ securityScheme, new string[] { } }
});
});
}
@@ -19,6 +19,7 @@
<PackageReference Include="IdentityModel" Version="7.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
@@ -0,0 +1,219 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Hua.Abp.Demo.Swagger;
public class OpenIddictDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var paths = new Dictionary<string, OpenApiPathItem>
{
["/.well-known/openid-configuration"] = new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
[OperationType.Get] = new OpenApiOperation
{
Tags = new List<OpenApiTag> { new OpenApiTag { Name = "OpenID Connect" } },
Summary = "获取 OpenID Connect 发现文档",
Responses = new OpenApiResponses
{
["200"] = new OpenApiResponse
{
Description = "成功",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/json"] = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["issuer"] = new OpenApiSchema { Type = "string", Description = "发行者 URL", Example = new OpenApiString("https://localhost:44322") },
["authorization_endpoint"] = new OpenApiSchema { Type = "string", Description = "授权端点 URL", Example = new OpenApiString("https://localhost:44322/connect/authorize") },
["token_endpoint"] = new OpenApiSchema { Type = "string", Description = "令牌端点 URL", Example = new OpenApiString("https://localhost:44322/connect/token") },
["jwks_uri"] = new OpenApiSchema { Type = "string", Description = "公钥集 URL", Example = new OpenApiString("https://localhost:44322/.well-known/jwks") },
["userinfo_endpoint"] = new OpenApiSchema { Type = "string", Description = "用户信息端点 URL", Example = new OpenApiString("https://localhost:44322/connect/userinfo") },
["scopes_supported"] = new OpenApiSchema { Type = "array", Description = "支持的作用域", Items = new OpenApiSchema { Type = "string" } },
["claims_supported"] = new OpenApiSchema { Type = "array", Description = "支持的声明", Items = new OpenApiSchema { Type = "string" } },
["response_types_supported"] = new OpenApiSchema { Type = "array", Description = "支持的响应类型", Items = new OpenApiSchema { Type = "string" } }
}
}
}
}
}
}
}
}
},
["/connect/token"] = new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
[OperationType.Post] = new OpenApiOperation
{
Tags = new List<OpenApiTag> { new OpenApiTag { Name = "OpenID Connect" } },
Summary = "获取访问令牌 (Access Token)",
Description = "使用授权码、密码或刷新令牌来换取访问令牌",
RequestBody = new OpenApiRequestBody
{
Content = new Dictionary<string, OpenApiMediaType>
{
["application/x-www-form-urlencoded"] = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["grant_type"] = new OpenApiSchema { Type = "string", Description = "授权类型 (password, client_credentials, authorization_code, refresh_token)", Example = new OpenApiString("password") },
["username"] = new OpenApiSchema { Type = "string", Description = "用户名 (密码模式必填)", Example = new OpenApiString("admin") },
["password"] = new OpenApiSchema { Type = "string", Description = "密码 (密码模式必填)", Example = new OpenApiString("1q2w3E*") },
["scope"] = new OpenApiSchema { Type = "string", Description = "请求的作用域 (空格分隔)", Example = new OpenApiString("openid profile email offline_access Demo") },
["client_id"] = new OpenApiSchema { Type = "string", Description = "客户端 ID", Example = new OpenApiString("Demo_App") },
["client_secret"] = new OpenApiSchema { Type = "string", Description = "客户端密钥 (如适用)" },
["code"] = new OpenApiSchema { Type = "string", Description = "授权码 (授权码模式必填)" },
["redirect_uri"] = new OpenApiSchema { Type = "string", Description = "重定向 URI" },
["refresh_token"] = new OpenApiSchema { Type = "string", Description = "刷新令牌 (刷新模式必填)" }
},
Required = new HashSet<string> { "grant_type" }
}
}
}
},
Responses = new OpenApiResponses
{
["200"] = new OpenApiResponse
{
Description = "成功",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/json"] = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["access_token"] = new OpenApiSchema { Type = "string", Description = "访问令牌 (JWT)" },
["token_type"] = new OpenApiSchema { Type = "string", Description = "令牌类型 (通常为 Bearer)", Example = new OpenApiString("Bearer") },
["expires_in"] = new OpenApiSchema { Type = "integer", Format = "int32", Description = "过期时间 (秒)", Example = new OpenApiInteger(3600) },
["scope"] = new OpenApiSchema { Type = "string", Description = "实际授予的作用域" },
["id_token"] = new OpenApiSchema { Type = "string", Description = "身份令牌 (JWT,仅在请求 openid scope 时返回)" },
["refresh_token"] = new OpenApiSchema { Type = "string", Description = "刷新令牌 (仅在请求 offline_access scope 时返回)" }
}
}
}
}
},
["400"] = new OpenApiResponse
{
Description = "请求无效",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/json"] = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["error"] = new OpenApiSchema { Type = "string", Description = "错误代码" },
["error_description"] = new OpenApiSchema { Type = "string", Description = "错误描述" }
}
}
}
}
}
}
}
}
},
["/connect/authorize"] = new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
[OperationType.Get] = new OpenApiOperation
{
Tags = new List<OpenApiTag> { new OpenApiTag { Name = "OpenID Connect" } },
Summary = "发起授权请求",
Description = "重定向用户到此端点以开始登录流程",
Parameters = new List<OpenApiParameter>
{
new OpenApiParameter { Name = "response_type", In = ParameterLocation.Query, Required = true, Schema = new OpenApiSchema { Type = "string", Example = new OpenApiString("code") }, Description = "响应类型 (如 code)" },
new OpenApiParameter { Name = "client_id", In = ParameterLocation.Query, Required = true, Schema = new OpenApiSchema { Type = "string", Example = new OpenApiString("Demo_App") }, Description = "客户端 ID" },
new OpenApiParameter { Name = "redirect_uri", In = ParameterLocation.Query, Schema = new OpenApiSchema { Type = "string" }, Description = "登录成功后的重定向 URI" },
new OpenApiParameter { Name = "scope", In = ParameterLocation.Query, Schema = new OpenApiSchema { Type = "string", Example = new OpenApiString("openid profile") }, Description = "请求的作用域" },
new OpenApiParameter { Name = "state", In = ParameterLocation.Query, Schema = new OpenApiSchema { Type = "string" }, Description = "状态值 (用于防止 CSRF)" }
},
Responses = new OpenApiResponses
{
["200"] = new OpenApiResponse { Description = "成功 (返回 HTML 登录页)" }
}
}
}
},
["/connect/userinfo"] = new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
[OperationType.Get] = new OpenApiOperation
{
Tags = new List<OpenApiTag> { new OpenApiTag { Name = "OpenID Connect" } },
Summary = "获取用户信息",
Description = "使用 Access Token 获取当前用户的详细信息",
Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] { }
}
}
},
Responses = new OpenApiResponses
{
["200"] = new OpenApiResponse
{
Description = "成功",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/json"] = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["sub"] = new OpenApiSchema { Type = "string", Description = "用户唯一标识 (ID)" },
["name"] = new OpenApiSchema { Type = "string", Description = "用户名" },
["email"] = new OpenApiSchema { Type = "string", Description = "邮箱" },
["email_verified"] = new OpenApiSchema { Type = "boolean", Description = "邮箱是否已验证" }
}
}
}
}
}
}
}
}
}
};
foreach (var path in paths)
{
if (!swaggerDoc.Paths.ContainsKey(path.Key))
{
swaggerDoc.Paths.Add(path.Key, path.Value);
}
}
}
}