在针对 Azure AD 注销 ASP.NET 核心 MVC 应用程序时如何修复错误 404?
How to fix error 404 when logging out on an ASP.NET Core MVC app against Azure AD?
我正在将 ASP.NET Core MVC 3.0 应用程序集成到 Azure AD 以进行身份验证和授权,一切正常,但是当我尝试注销时,一旦 login.microsoftonline.com
将我注销,它重定向到我的应用程序,然后出现以下错误:
No webpage was found for the web address:
https://localhost:5002/Account/SignOut?page=%2FAccount%2FSignedOut
我用来调用注销过程的路径是/AzureAD/Account/SignOut
。
appsettings.json
的内容:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "[OMITTED]",
"TenantId": "[OMITTED]",
"ClientId": "[OMITTED]",
"CallbackPath": "/signin-oidc"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
这些是我的内容 Startup.cs
class:
using System.Collections.Generic;
using System.Globalization;
using MySite.WebSite.Helpers;
using MySite.WebSite.Models.Validators;
using MySite.WebSite.Models.ViewModels;
using FluentValidation;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
namespace MySite.WebSite
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<CookieAuthenticationOptions>(AzureADDefaults.CookieScheme, options =>
{
options.AccessDeniedPath = "/Home/AccessDenied";
options.LogoutPath = "/";
});
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority += "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
services.AddLocalization(options => options.ResourcesPath = "Resources");
services
.AddControllersWithViews(options => options.Filters.Add(GetAuthorizeFilter()))
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization()
.AddFluentValidation();
services.AddTransient<IValidator<ContactIndexViewModel>, ContactIndexViewModelValidator>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRequestLocalization(GetLocalizationOptions());
app.UseStaticFiles(GetStaticFileOptions());
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
private RequestLocalizationOptions GetLocalizationOptions()
{
var cookie_request_culture_provider = new CookieRequestCultureProvider
{
CookieName = "UserCulture"
};
var providers = new List<IRequestCultureProvider>()
{
cookie_request_culture_provider,
new AcceptLanguageHeaderRequestCultureProvider()
};
var result = new RequestLocalizationOptions
{
RequestCultureProviders = providers,
SupportedCultures = Cultures.SupportedCultures,
SupportedUICultures = Cultures.SupportedCultures,
DefaultRequestCulture = new RequestCulture(Cultures.DefaultCulture)
};
return result;
}
private StaticFileOptions GetStaticFileOptions()
{
var result = new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "text/plain"
};
return result;
}
private AuthorizeFilter GetAuthorizeFilter()
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
var result = new AuthorizeFilter(policy);
return result;
}
}
}
Microsoft.AspNetCore.Authentication.AzureAD.UI
结果是 this is a known issue;该包在 ASP.NET Core 中实现了 Azure AD authentication/authorization 流程,其中一部分是嵌入式 AccountController
(区域 AzureAD
),它将登录 - 注销过程从您的肩膀。问题是,SignOut
操作在注销过程完成后将重定向硬编码到 /Account/SignOut?page=%2FAccount%2FSignedOut
,这就是问题所在。
我设法通过实现一个小的 AccountController(没有区域)并添加一个处理来自 Microsoft.AspNetCore.Authentication.AzureAD.UI
的 AccountController
的重定向的 SignOut
操作来解决它:
[AllowAnonymous]
public class AccountController : Controller
{
[HttpGet]
public IActionResult SignOut(string page)
{
return RedirectToAction("Index", "Home");
}
}
在(至少)您应该添加的最新版本中
endpoints.MapRazorPages();
在 Startup 的 Configure() 方法中。这会为您处理路线。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();//ADD THIS LINE
});
我花了半天时间查了一下,发现在我们迁移到 .NET Core 3.0 时它坏了。
我们还迁移到了 Microsoft Identity,因为 AzureADB2C UI 被标记为已过时。因此,在 ConfigureServices 中,我们添加了:
services.AddRazorPages()
.AddMicrosoftIdentityUI();
...正如 Paul 所说,在 Configure 中我们添加了:
endpoints.MapRazorPages();
这为我们解决了问题。我们从使用模板中的 Microsoft Identity 身份验证创建全新的 MVC Web 应用程序中获得了提示,并比较了我们的 ConfigureServices / Configure 方法的差异。
您必须将 asp-area="AzureAD" 值更改为 asp-area="MicrosoftIdentity"[= _LoginPartial.cshtml 文件中的 14=]
我正在将 ASP.NET Core MVC 3.0 应用程序集成到 Azure AD 以进行身份验证和授权,一切正常,但是当我尝试注销时,一旦 login.microsoftonline.com
将我注销,它重定向到我的应用程序,然后出现以下错误:
No webpage was found for the web address:
https://localhost:5002/Account/SignOut?page=%2FAccount%2FSignedOut
我用来调用注销过程的路径是/AzureAD/Account/SignOut
。
appsettings.json
的内容:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "[OMITTED]",
"TenantId": "[OMITTED]",
"ClientId": "[OMITTED]",
"CallbackPath": "/signin-oidc"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
这些是我的内容 Startup.cs
class:
using System.Collections.Generic;
using System.Globalization;
using MySite.WebSite.Helpers;
using MySite.WebSite.Models.Validators;
using MySite.WebSite.Models.ViewModels;
using FluentValidation;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
namespace MySite.WebSite
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<CookieAuthenticationOptions>(AzureADDefaults.CookieScheme, options =>
{
options.AccessDeniedPath = "/Home/AccessDenied";
options.LogoutPath = "/";
});
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority += "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
services.AddLocalization(options => options.ResourcesPath = "Resources");
services
.AddControllersWithViews(options => options.Filters.Add(GetAuthorizeFilter()))
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization()
.AddFluentValidation();
services.AddTransient<IValidator<ContactIndexViewModel>, ContactIndexViewModelValidator>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRequestLocalization(GetLocalizationOptions());
app.UseStaticFiles(GetStaticFileOptions());
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
private RequestLocalizationOptions GetLocalizationOptions()
{
var cookie_request_culture_provider = new CookieRequestCultureProvider
{
CookieName = "UserCulture"
};
var providers = new List<IRequestCultureProvider>()
{
cookie_request_culture_provider,
new AcceptLanguageHeaderRequestCultureProvider()
};
var result = new RequestLocalizationOptions
{
RequestCultureProviders = providers,
SupportedCultures = Cultures.SupportedCultures,
SupportedUICultures = Cultures.SupportedCultures,
DefaultRequestCulture = new RequestCulture(Cultures.DefaultCulture)
};
return result;
}
private StaticFileOptions GetStaticFileOptions()
{
var result = new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "text/plain"
};
return result;
}
private AuthorizeFilter GetAuthorizeFilter()
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
var result = new AuthorizeFilter(policy);
return result;
}
}
}
Microsoft.AspNetCore.Authentication.AzureAD.UI
结果是 this is a known issue;该包在 ASP.NET Core 中实现了 Azure AD authentication/authorization 流程,其中一部分是嵌入式 AccountController
(区域 AzureAD
),它将登录 - 注销过程从您的肩膀。问题是,SignOut
操作在注销过程完成后将重定向硬编码到 /Account/SignOut?page=%2FAccount%2FSignedOut
,这就是问题所在。
我设法通过实现一个小的 AccountController(没有区域)并添加一个处理来自 Microsoft.AspNetCore.Authentication.AzureAD.UI
的 AccountController
的重定向的 SignOut
操作来解决它:
[AllowAnonymous]
public class AccountController : Controller
{
[HttpGet]
public IActionResult SignOut(string page)
{
return RedirectToAction("Index", "Home");
}
}
在(至少)您应该添加的最新版本中
endpoints.MapRazorPages();
在 Startup 的 Configure() 方法中。这会为您处理路线。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();//ADD THIS LINE
});
我花了半天时间查了一下,发现在我们迁移到 .NET Core 3.0 时它坏了。
我们还迁移到了 Microsoft Identity,因为 AzureADB2C UI 被标记为已过时。因此,在 ConfigureServices 中,我们添加了:
services.AddRazorPages()
.AddMicrosoftIdentityUI();
...正如 Paul 所说,在 Configure 中我们添加了:
endpoints.MapRazorPages();
这为我们解决了问题。我们从使用模板中的 Microsoft Identity 身份验证创建全新的 MVC Web 应用程序中获得了提示,并比较了我们的 ConfigureServices / Configure 方法的差异。
您必须将 asp-area="AzureAD" 值更改为 asp-area="MicrosoftIdentity"[= _LoginPartial.cshtml 文件中的 14=]