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.Swashbuckle;
|
||||||
using Volo.Abp.Studio.Client.AspNetCore;
|
using Volo.Abp.Studio.Client.AspNetCore;
|
||||||
using Volo.Abp.Security.Claims;
|
using Volo.Abp.Security.Claims;
|
||||||
|
using Hua.Abp.Demo.Swagger;
|
||||||
|
|
||||||
namespace Hua.Abp.Demo;
|
namespace Hua.Abp.Demo;
|
||||||
|
|
||||||
@@ -135,6 +136,12 @@ public class DemoHttpApiHostModule : AbpModule
|
|||||||
|
|
||||||
var configuration = context.Services.GetConfiguration();
|
var configuration = context.Services.GetConfiguration();
|
||||||
context.Services.AddAuthentication()
|
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 =>
|
.AddOpenIdConnect("WeGit", "Login with WeGit", options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://git.we965.cn";
|
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.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo API", Version = "v1" });
|
||||||
options.DocInclusionPredicate((docName, description) => true);
|
options.DocInclusionPredicate((docName, description) => true);
|
||||||
options.CustomSchemaIds(type => type.FullName);
|
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="IdentityModel" Version="7.0.0" />
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.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="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.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" />
|
||||||
<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