asp.net-core - 使用 SwaggerUI 通过自己的受保护 API 端点对 IdentiyServer 4 进行身份验证
问题描述
我对 IdentityServer4 比较陌生,但我浏览了文档并设法进行了设置。
在我的场景中,我想使用 IdentityServer4 并保护身份服务器中的其他端点。我按照文档使用此处描述的客户端凭据。我没有使用单独的 API,而是按照此处的文档来保护身份服务器中的 API 端点,该端点按预期工作。
我还想使用 Swagger 为这些额外的端点提供文档。但不幸的是,我不知道如何正确设置配置。出现“授权”按钮,我可以输入客户端和密码,也可以登录,这工作正常,但每当我尝试使用 Swagger 执行操作时,我都会收到 401 Unauthorized 错误。
我的示例项目如下所示:
程序.cs
public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
.Enrich.FromLogContext()
// uncomment to write to Azure diagnostics stream
//.WriteTo.File(
// @"D:\home\LogFiles\Application\identityserver.txt",
// fileSizeLimitBytes: 1_000_000,
// rollOnFileSizeLimit: true,
// shared: true,
// flushToDiskInterval: TimeSpan.FromSeconds(1))
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
.CreateLogger();
try
{
Log.Information("Starting host...");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly.");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
启动.cs
public class Startup
{
public IWebHostEnvironment Environment { get; }
public Startup(IWebHostEnvironment environment)
{
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services
.AddControllers();
services
.AddIdentityServer(options =>
{
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
options.EmitStaticAudienceClaim = true;
})
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
services.AddLocalApiAuthentication();
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo {Title = "Protected API", Version = "v1"});
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
In = ParameterLocation.Header,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("https://localhost:5001/connect/authorize"),
TokenUrl = new Uri("https://localhost:5001/connect/token"),
Scopes = new Dictionary<string, string>
{
{"IdentityServerApi", "Demo API - full access"}
}
}
}
});
});
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
options.OAuthClientId("client");
options.OAuthAppName("Demo API - Swagger");
options.OAuthUsePkce();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireAuthorization(IdentityServerConstants.LocalApi.PolicyName); // auth attribute for all controllers!
});
}
}
配置文件
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId()
};
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope(IdentityServerConstants.LocalApi.ScopeName, "My API"),
};
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { IdentityServerConstants.LocalApi.ScopeName }
}
};
}
LocalApiController.cs
[ApiController]
[Route("localApi")]
public class LocalApiController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
return Ok("ok");
}
}
解决方案
我忘记在 swagger 文档中添加身份验证信息。
添加以下过滤器并按照此处所述进行注册后,一切正常
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var hasAuthorize =
context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
|| context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
if (hasAuthorize)
{
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[
new OpenApiSecurityScheme {Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "oauth2"}
}
] = new[] {"api1"}
}
};
}
}
}
推荐阅读
- python - 套接字连接中的 Client.py 不起作用
- javascript - javascript何时将数字转换为指数符号?什么是断点?
- javascript - ESLint 不知道 Element UI 的组件
- android - Google fit 活动未从 Android 设备提供任何“静止”类型的活动
- reactjs - 如何在 nextjs 中将同一页面与两个 url 链接
- jsonpath - Json 字符串的 JsonPath 表达式?
- angular - 如何从内部订阅角度返回值?
- vue.js - VueJS - 收到成功消息后如何自动关闭弹出窗口
- javascript - 使用 onClick 按其值对数组中的对象进行排序
- yii2 - 登录表单无法在 yii2 中验证