OpenIddict - 在同一项目中托管身份验证服务器和网络 api 资源
OpenIddict - hosting auth server and web api resource in same project
我想使用 OpenIddict 实现 OpenIdConnect/Oauth2 服务器,以保护 .NET 核心 API 应用程序。我见过的大多数示例将它们作为单独的项目实现。
客户端应用程序是一个 SPA,我们正在使用隐式流。
我的解决方案基于此处 OpenIddict 示例中显示的代码:
https://github.com/openiddict/openiddict-samples
对于我正在处理的项目,理想情况下,Auth 服务器和 API 使用相同的端口并在同一个项目中。 (客户的要求之一是他们不希望配置另一台服务器,因为他们拥有 API 资源并且它将位于同一台服务器上)
我已经配置了 OpenIddict 并将其与我们的 API 项目相结合。几乎一切正常 - API 端点受 [Authorize] 属性保护,并阻止访问受保护的 API 端点。但是,当 API 资源被保护时,返回的结果不是 401 Unauthorized HTTP 状态码,而是 Auth 服务器本身的 HTML 登录页面。
这是我的 Startup.cs 文件中的相关设置代码:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseIdentity();
app.UseCors("AllowAll");
//app.UseCors(builder =>
//{
// builder.AllowAnyOrigin();//)WithOrigins("http://localhost:9000");
// builder.WithMethods("GET","POST", "PUT", "DELETE", "OPTIONS");
// builder.WithHeaders("Authorization");
//});
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), branch =>
{
branch.UseIdentity();
});
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), branch =>
{
branch.UseOAuthValidation();
});
app.UseOpenIddict();
#region Adding resource config here (api)
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseOAuthIntrospection(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.Authority = "http://localhost:5000";
options.Audiences.Add("resource-server-1");
options.ClientId = "resource-server-1";
options.ClientSecret = "846B62D0-DEF9-4215-A99D-86E6B8DAB342";
});
//app.UseCors(builder => {
// builder.WithOrigins("http://localhost:9000");
// builder.WithMethods("GET");
// builder.WithHeaders("Authorization");
//});
#endregion
app.UseMvcWithDefaultRoute();
// Seed the database with the sample applications.
// Note: in a real world application, this step should be part of a setup script.
InitializeAsync(app.ApplicationServices, CancellationToken.None).GetAwaiter().GetResult();
}
private async Task InitializeAsync(IServiceProvider services, CancellationToken cancellationToken)
{
// Create a new service scope to ensure the database context is correctly disposed when this methods returns.
using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
//await context.Database.EnsureCreatedAsync();
var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();
if (await manager.FindByClientIdAsync("MySPA", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "MySPA",
DisplayName = "MySPA",
LogoutRedirectUri = "http://localhost:9000/signout-oidc",
RedirectUri = "http://localhost:9000/signin-oidc"
};
await manager.CreateAsync(application, cancellationToken);
}
if (await manager.FindByClientIdAsync("resource-server-1", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "resource-server-1"
};
await manager.CreateAsync(application, "846B62D0-DEF9-4215-A99D-86E6B8DAB342", cancellationToken);
}
}
}
不确定如何在同一个项目中同时实现这些。如前所述,所有 "works" 除了 API 都返回 HTML 登录页面而不是所需的 HTTP 状态
app.UseIdentity();
在您的管道中出现两次,这违背了在 app.UseWhen()
分支构建器中使用 branch.UseIdentity()
的全部目的(即确保 Identity 注册的 cookies 中间件不是为您的 API 端点调用)。
删除第一个出现的地方,它应该可以工作。
您将 AutomaticChallenge 设置为 true
。根据 documentation
this flag indicates that the middleware should redirect the browser to the LoginPath or AccessDeniedPath when authorization fails.
因此,通过将其设置为 false,它不会重定向到登录。
我想使用 OpenIddict 实现 OpenIdConnect/Oauth2 服务器,以保护 .NET 核心 API 应用程序。我见过的大多数示例将它们作为单独的项目实现。
客户端应用程序是一个 SPA,我们正在使用隐式流。
我的解决方案基于此处 OpenIddict 示例中显示的代码: https://github.com/openiddict/openiddict-samples
对于我正在处理的项目,理想情况下,Auth 服务器和 API 使用相同的端口并在同一个项目中。 (客户的要求之一是他们不希望配置另一台服务器,因为他们拥有 API 资源并且它将位于同一台服务器上)
我已经配置了 OpenIddict 并将其与我们的 API 项目相结合。几乎一切正常 - API 端点受 [Authorize] 属性保护,并阻止访问受保护的 API 端点。但是,当 API 资源被保护时,返回的结果不是 401 Unauthorized HTTP 状态码,而是 Auth 服务器本身的 HTML 登录页面。
这是我的 Startup.cs 文件中的相关设置代码:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseIdentity();
app.UseCors("AllowAll");
//app.UseCors(builder =>
//{
// builder.AllowAnyOrigin();//)WithOrigins("http://localhost:9000");
// builder.WithMethods("GET","POST", "PUT", "DELETE", "OPTIONS");
// builder.WithHeaders("Authorization");
//});
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), branch =>
{
branch.UseIdentity();
});
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), branch =>
{
branch.UseOAuthValidation();
});
app.UseOpenIddict();
#region Adding resource config here (api)
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseOAuthIntrospection(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.Authority = "http://localhost:5000";
options.Audiences.Add("resource-server-1");
options.ClientId = "resource-server-1";
options.ClientSecret = "846B62D0-DEF9-4215-A99D-86E6B8DAB342";
});
//app.UseCors(builder => {
// builder.WithOrigins("http://localhost:9000");
// builder.WithMethods("GET");
// builder.WithHeaders("Authorization");
//});
#endregion
app.UseMvcWithDefaultRoute();
// Seed the database with the sample applications.
// Note: in a real world application, this step should be part of a setup script.
InitializeAsync(app.ApplicationServices, CancellationToken.None).GetAwaiter().GetResult();
}
private async Task InitializeAsync(IServiceProvider services, CancellationToken cancellationToken)
{
// Create a new service scope to ensure the database context is correctly disposed when this methods returns.
using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
//await context.Database.EnsureCreatedAsync();
var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();
if (await manager.FindByClientIdAsync("MySPA", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "MySPA",
DisplayName = "MySPA",
LogoutRedirectUri = "http://localhost:9000/signout-oidc",
RedirectUri = "http://localhost:9000/signin-oidc"
};
await manager.CreateAsync(application, cancellationToken);
}
if (await manager.FindByClientIdAsync("resource-server-1", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "resource-server-1"
};
await manager.CreateAsync(application, "846B62D0-DEF9-4215-A99D-86E6B8DAB342", cancellationToken);
}
}
}
不确定如何在同一个项目中同时实现这些。如前所述,所有 "works" 除了 API 都返回 HTML 登录页面而不是所需的 HTTP 状态
app.UseIdentity();
在您的管道中出现两次,这违背了在 app.UseWhen()
分支构建器中使用 branch.UseIdentity()
的全部目的(即确保 Identity 注册的 cookies 中间件不是为您的 API 端点调用)。
删除第一个出现的地方,它应该可以工作。
您将 AutomaticChallenge 设置为 true
。根据 documentation
this flag indicates that the middleware should redirect the browser to the LoginPath or AccessDeniedPath when authorization fails.
因此,通过将其设置为 false,它不会重定向到登录。