Identityserver4 API-аутентификация не работает должным образом
Я застрял с этой проблемой почти на месяц, поэтому любая помощь приветствуется. Давайте вернемся к самой проблеме: у меня есть сервер идентификации и API управления пользователями (на основе CRUD) в одном проекте. Сам сервер идентификации работает как страница входа / регистрации для других веб-сайтов (в настоящее время у меня есть только один веб-сайт MVC ASP.NET Framework). API используется для извлечения и обновления профиля пользователя из проекта MVC и мобильного приложения. Сервер идентификации и проект MVC поддерживаются док-контейнерами.
Аутентификация API выполняется через маркер канала идентификации сервера. Таким образом, аутентификация API отлично работает на локальном хосте, однако, когда я развертываю сервер идентификации на экземплярах контейнеров Azure, API перестает работать как из MVC, так и из Postman. Я получаю ошибку:
Произошло необработанное исключение при обработке запроса. WinHttpException: тайм-аут операции
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() HttpRequestException: при отправке запроса произошла ошибка.
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () IOException: IDX10804: невозможно получить документ из: ' http://taxrefund-identity.westeurope.azurecontainer.io/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever + d__8.MoveNext () InvalidOperationException: IDX10803: невозможно получить конфигурацию из: ' 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 (либо с учетными данными клиента, либо с помощью пароля / имени пользователя владельца ресурса), а затем использую его в заголовке авторизации для последующих запросов.
Я застрял с этой проблемой почти месяц - сейчас это расстраивает. Я прочитал все сообщения, связанные с этой проблемой, и ни одно из решений не помогло. Я пытался перейти на более ранние версии system.net.http, различные сертификаты и другие решения, которые помогали другим.
Также конечная точка без атрибута [Authorize] работает просто отлично.
Единственное, что я не пробовал, это установить SSL-сертификат и сделать мои URL-адреса https - я прочитал, что это не должно влиять на функциональность сервера идентификации. У меня сейчас нет реальной необходимости в этом, поэтому дайте мне знать, если это необходимо.
Если мне нужна дополнительная информация, дайте мне знать.
Очень признателен.
1 ответ
Окончательное решение состояло в том, чтобы изменить URL авторизации в AddIdentityServerAuthentication
от:
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-интерфейс работают в одном и том же экземпляре / процессе контейнера, поэтому он не может получить к себе доступ через URL-адрес DNS, а может получить доступ к самому себе через localhost
URL.