1.OpenID Connect相关接口添加到swagger文档
2.添加jwt认证
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user