当 Identity Razor 页面按预期工作时,使用 MVC 页面获得 401 Unauthorized

Getting 401 Unauthorized with MVC Pages while Identity Razor pages work as expected

背景

我正在进行 POC 以了解 Angular、Razor 和 MVC 页面是否可以在 Web 应用程序中无缝工作。我从名为“ASP.NET Core with Angular”的 Visual Studio 模板开始。我选择了“个人帐户”以包含默认身份验证功能。这会创建一个带有安全网络 API 端点 (WeatherForecast) 的 Angular 应用程序,并提供基本的用户注册、登录、注销、用户个人资料页面等内置功能。到目前为止,当我尝试从受保护的 API (WeatherForecast) 获取数据 我被重定向到 Identiy/Account/Login razor 页面,在那里我可以登录,然后被重定向回 Angular 我可以看到数据已返回并且网格人口稠密。至此一切正常。

问题

我添加了一个带有基本“Hello World”HTML 视图的 DemoController class。当我尝试使用 /demo 访问这个新页面时,它按预期工作。但是,当我将 [Authorize] 属性应用于控制器时,我得到 401 Unauthorized。我在服务器端检查 User.IsAuthenticated 属性 设置为 false 尽管之前已成功登录。现在有趣的观察是用户个人资料页面(受保护并且只有在有活动登录时才能工作)工作正常。

请注意,来自 Angular 的所有 API 调用问题都使用 JWT 不记名令牌并且工作正常。当我尝试访问用户个人资料页面时,它不使用 JWT,而是使用 cookie 进行身份验证。对 /demo 页面的 GET 请求在 headers 中也有所有这些 cookie,但仍然遇到 401.

我花了很多时间浏览文章,搜索网络但没有成功。我们发现的最后一件事是: 但这也无济于事。

该项目是使用Visual Studio 2022、.net core 6.0 创建的。这是供您参考的 Program.cs 文件:

    using CoreAngular.Data;
    using CoreAngular.Models;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Identity.UI;
    using Microsoft.EntityFrameworkCore;

    var builder = WebApplication.CreateBuilder(args);

    // Add services to the container.
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();

    builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    builder.Services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

    builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

    builder.Services.AddControllersWithViews();
    builder.Services.AddRazorPages();

    var app = builder.Build();

    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseMigrationsEndPoint();
    }
    else
    {
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseAuthentication();
    app.UseIdentityServer();
    app.UseAuthorization();

    app.MapControllerRoute(
        name: "default",
        pattern: "{controller}/{action=Index}/{id?}");

    app.MapRazorPages();

    app.MapFallbackToFile("index.html"); ;

    app.Run();

这里已经回答了:

事实证明,使用 IdentityServer 扩展方法添加了一个策略方案,使得只有 /Identity 页面具有 cookie 身份验证。其余默认为JWT。

我们可以通过添加我们自己的策略来自定义它,如下所示:

builder.Services.AddAuthentication()
    .AddIdentityServerJwt()
    .AddPolicyScheme("ApplicationDefinedAuthentication", null, options =>
    {
        options.ForwardDefaultSelector = (context) =>
        {
            if (context.Request.Path.StartsWithSegments(new PathString("/Identity"), StringComparison.OrdinalIgnoreCase) ||
                context.Request.Path.StartsWithSegments(new PathString("/demo"), StringComparison.OrdinalIgnoreCase))
                return IdentityConstants.ApplicationScheme;
            else
                return IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
        };
    });

// Use own policy scheme instead of default policy scheme that was set in method AddIdentityServerJwt 
builder.Services.Configure<AuthenticationOptions>(options => options.DefaultScheme = "ApplicationDefinedAuthentication");