Identityserver4 API 身份验证未按预期工作
Identityserver4 API authentication doesn't work as expected
我被这个问题困扰了将近一个月,所以非常感谢您的帮助。让我们来看看问题本身:
我在一个项目中有一个身份服务器和用户管理 API(基于 CRUD)。身份服务器本身作为其他网站的 login/register 页面(我目前只有一个 ASP.NET 框架 MVC 网站)。 API 用于从 MVC 项目和移动应用程序中检索和更新用户配置文件。身份服务器和 MVC 项目由 docker 个容器支持。
API 身份验证是通过身份服务器持有者令牌完成的。因此,API 身份验证在本地主机上完美运行,但是,当我将身份服务器部署到 Azure 容器实例时,API 从 MVC 和 Postman 停止工作。我得到的错误是:
An unhandled exception occurred while processing the request.
WinHttpException: The operation timed out
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
HttpRequestException: An error occurred while sending the request.
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
IOException: IDX10804: Unable to retrieve document from: 'http://taxrefund-identity.westeurope.azurecontainer.io/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever+d__8.MoveNext()
InvalidOperationException: IDX10803: Unable to obtain configuration from: 'http://taxrefund-identity.westeurope.azurecontainer.io/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.ConfigurationManager+d__24.MoveNext()
最奇怪的是,我可以毫无问题地通过浏览器访问发现端点。
我的 ConfigureServices 方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlServerOptionsAction: sqlOptions =>{
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
}), ServiceLifetime.Scoped
);
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddTransient<IEmailSender, EmailSender>();
services.AddScoped<DatabaseInitializer>();
services.AddCors();
// Adds IdentityServer
var cert = new X509Certificate2(Path.Combine(Environment.ContentRootPath, "idsrv3test.pfx"), "idsrv3test");
services.AddIdentityServer()
.AddSigningCredential(cert)
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>()
.Services.AddTransient<IProfileService, ProfileService>();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.ClientId = "**";
options.ClientSecret = "**";
})
.AddMicrosoftAccount("Microsoft", options =>
{
options.ClientId = "**";
options.ClientSecret = "**";
});
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://taxrefund-identity.westeurope.azurecontainer.io/";
o.ApiName = "Profile.API";
o.ApiSecret = "**";
o.RequireHttpsMetadata = false;
});
services.AddMvc();
services.AddAntiforgery();
}
配置方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, RoleManager<IdentityRole> roleManager, ApplicationDbContext context, UserManager<ApplicationUser> userManager)
{
loggerFactory.AddDebug();
loggerFactory.AddConsole(LogLevel.Trace);
loggerFactory.AddFile("logs.txt");
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
app.UseCors(policy =>
{
policy.AllowCredentials();
policy.AllowAnyOrigin();
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.WithExposedHeaders("WWW-Authenticate");
});
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
context.Database.Migrate();
DatabaseInitializer.SeedData(roleManager);
}
API资源配置:
new ApiResource("Profile.API", "Profile management API")
{
UserClaims = { ClaimTypes.Role },
ApiSecrets =
{
new Secret("**".Sha256())
}
}
我这样保护我的 API:
[Authorize(AuthenticationSchemes = "Bearer")]
[Route("api/Users")]
[Produces("application/json")]
public class ApplicationUsersAPIController : ControllerBase
要访问它,我从 /connect/token 端点请求令牌(使用客户端凭据或资源所有者 password/username),然后在授权 header 中使用它进行后续请求。
这个问题困扰我将近一个月了 - 现在越来越令人沮丧了。我已阅读与此问题相关的所有帖子,none 的解决方案有所帮助。我已经尝试降级到 system.net.http 的早期版本、不同的证书和更多帮助他人的解决方案。
此外,没有 [Authorize] 属性的端点工作正常。
我唯一没有尝试的是安装 SSL 证书并将我的网址设为 https - 我读到这不会影响身份服务器的功能。我现在没有实际需要,所以如果有必要请告诉我。
如果需要我提供更多信息,请告诉我。
非常感谢。
最终解决方案是将 AddIdentityServerAuthentication
中的权限 URL 更改为:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://taxrefund-identity.westeurope.azurecontainer.io/";
o.ApiName = "Profile.API";
o.ApiSecret = "**";
o.RequireHttpsMetadata = false;
});
收件人:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://localhost/"; //crucial part
o.ApiName = "Profile.API";
o.ApiSecret = "**";
o.RequireHttpsMetadata = false;
});
这实际上是有道理的,因为在这种情况下,身份服务器和 API 是 运行 在同一个容器 instance/process 中,因此,它无法访问它self 通过 DNS URL,相反,它可以通过 localhost
URL.
访问自己
我被这个问题困扰了将近一个月,所以非常感谢您的帮助。让我们来看看问题本身: 我在一个项目中有一个身份服务器和用户管理 API(基于 CRUD)。身份服务器本身作为其他网站的 login/register 页面(我目前只有一个 ASP.NET 框架 MVC 网站)。 API 用于从 MVC 项目和移动应用程序中检索和更新用户配置文件。身份服务器和 MVC 项目由 docker 个容器支持。
API 身份验证是通过身份服务器持有者令牌完成的。因此,API 身份验证在本地主机上完美运行,但是,当我将身份服务器部署到 Azure 容器实例时,API 从 MVC 和 Postman 停止工作。我得到的错误是:
An unhandled exception occurred while processing the request. WinHttpException: The operation timed out
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() HttpRequestException: An error occurred while sending the request.
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() IOException: IDX10804: Unable to retrieve document from: 'http://taxrefund-identity.westeurope.azurecontainer.io/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever+d__8.MoveNext() InvalidOperationException: IDX10803: Unable to obtain configuration from: 'http://taxrefund-identity.westeurope.azurecontainer.io/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.ConfigurationManager+d__24.MoveNext()
最奇怪的是,我可以毫无问题地通过浏览器访问发现端点。
我的 ConfigureServices 方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlServerOptionsAction: sqlOptions =>{
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
}), ServiceLifetime.Scoped
);
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddTransient<IEmailSender, EmailSender>();
services.AddScoped<DatabaseInitializer>();
services.AddCors();
// Adds IdentityServer
var cert = new X509Certificate2(Path.Combine(Environment.ContentRootPath, "idsrv3test.pfx"), "idsrv3test");
services.AddIdentityServer()
.AddSigningCredential(cert)
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>()
.Services.AddTransient<IProfileService, ProfileService>();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.ClientId = "**";
options.ClientSecret = "**";
})
.AddMicrosoftAccount("Microsoft", options =>
{
options.ClientId = "**";
options.ClientSecret = "**";
});
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://taxrefund-identity.westeurope.azurecontainer.io/";
o.ApiName = "Profile.API";
o.ApiSecret = "**";
o.RequireHttpsMetadata = false;
});
services.AddMvc();
services.AddAntiforgery();
}
配置方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, RoleManager<IdentityRole> roleManager, ApplicationDbContext context, UserManager<ApplicationUser> userManager)
{
loggerFactory.AddDebug();
loggerFactory.AddConsole(LogLevel.Trace);
loggerFactory.AddFile("logs.txt");
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
app.UseCors(policy =>
{
policy.AllowCredentials();
policy.AllowAnyOrigin();
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.WithExposedHeaders("WWW-Authenticate");
});
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
context.Database.Migrate();
DatabaseInitializer.SeedData(roleManager);
}
API资源配置:
new ApiResource("Profile.API", "Profile management API")
{
UserClaims = { ClaimTypes.Role },
ApiSecrets =
{
new Secret("**".Sha256())
}
}
我这样保护我的 API:
[Authorize(AuthenticationSchemes = "Bearer")]
[Route("api/Users")]
[Produces("application/json")]
public class ApplicationUsersAPIController : ControllerBase
要访问它,我从 /connect/token 端点请求令牌(使用客户端凭据或资源所有者 password/username),然后在授权 header 中使用它进行后续请求。
这个问题困扰我将近一个月了 - 现在越来越令人沮丧了。我已阅读与此问题相关的所有帖子,none 的解决方案有所帮助。我已经尝试降级到 system.net.http 的早期版本、不同的证书和更多帮助他人的解决方案。
此外,没有 [Authorize] 属性的端点工作正常。
我唯一没有尝试的是安装 SSL 证书并将我的网址设为 https - 我读到这不会影响身份服务器的功能。我现在没有实际需要,所以如果有必要请告诉我。
如果需要我提供更多信息,请告诉我。
非常感谢。
最终解决方案是将 AddIdentityServerAuthentication
中的权限 URL 更改为:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://taxrefund-identity.westeurope.azurecontainer.io/";
o.ApiName = "Profile.API";
o.ApiSecret = "**";
o.RequireHttpsMetadata = false;
});
收件人:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://localhost/"; //crucial part
o.ApiName = "Profile.API";
o.ApiSecret = "**";
o.RequireHttpsMetadata = false;
});
这实际上是有道理的,因为在这种情况下,身份服务器和 API 是 运行 在同一个容器 instance/process 中,因此,它无法访问它self 通过 DNS URL,相反,它可以通过 localhost
URL.