.NET Core 3.1 自定义 ClaimsPrincipal 无法与 AddAuthenticationSchemes 策略一起正常工作

问题描述 投票:0回答:1

我的 .NET Core 3.1 应用程序有一种身份验证方案(使用 OpenId Connect 的 cookie 身份验证),我计划添加另一种身份验证方案。

其他身份验证方案仅在一个特定区域允许,而当前身份验证方案应仅在所有其他地方允许。

为了满足这两个要求,我开始创建一个授权策略来授权用户的身份验证方案(默认区域的cookie身份验证)。

services.AddAuthorization(options =>
{
    options.AddPolicy("X", builder => builder.AddAuthenticationSchemes("Cookies").RequireAuthenticatedUser().Build());
});

...

app.UseEndpoints(endpoints =>
{
    endpoints.MapDefaultControllerRoute().RequireAuthorization("X");
});

到目前为止,一切都很好。所有端点都将通过身份验证方案进行授权。 但是,该解决方案还使用

IClaimsTransformation
的自定义实现,因为我需要重写
User.IsInRole
逻辑。

此自定义声明转换器注册如下

services.AddTransient<IClaimsTransformation, CustomClaimsTransformer>();

为了简单起见,

CustomClaimsTransformer
的内容只是返回一个带有附加逻辑的包装对象

public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
    var customPrincipal = new CustomClaimsPrincipal(principal) as ClaimsPrincipal;
    return Task.FromResult(customPrincipal);
}

问题:

当我在授权策略中使用

AddAuthenticationSchemes("Cookies")
时,我的
TransformAsync
CustomClaimsTransformer
会被调用,但我的
IsInRole
CustomClaimsPrincipal
永远不会被调用;这意味着每次
IsInRole
检查都会失败。

如果我省略

AddAuthenticationSchemes("Cookies")
部分,我的
IsInRole
方法会神奇地再次被调用。

在授权策略中使用所需的身份验证方案时,有什么方法可以使用我的

CustomClaimsPrincipal
吗?

c# .net asp.net-core authorization
1个回答
0
投票

请尝试使用下面的代码,它应该可以工作。我已经在我身边尝试过。

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace CustomAuthCore31
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // Add your custom claims transformation
            services.AddTransient<IClaimsTransformation, CustomClaimsTransformer>();

            // Add authentication services
            services.AddAuthentication("CustomCookies")
                .AddScheme<CookieAuthenticationOptions, CustomCookieAuthenticationHandler>("CustomCookies", options => {
                    // Configure your cookie settings here
                    options.LoginPath = "/Account/Login";
                    options.AccessDeniedPath = "/Account/AccessDenied";
                });

            services.AddAuthorization(options =>
            {
                options.AddPolicy("X", builder => builder.AddAuthenticationSchemes("CustomCookies").RequireAuthenticatedUser().Build());
            });
            ....
            services.AddControllersWithViews();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

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

            app.UseRouting();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute().RequireAuthorization("X");
            });
        }
    }

    public class CustomCookieAuthenticationHandler : CookieAuthenticationHandler
    {
        private readonly IClaimsTransformation _claimsTransformation;

        public CustomCookieAuthenticationHandler(IOptionsMonitor<CookieAuthenticationOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IClaimsTransformation claimsTransformation)
            : base(options, logger, encoder, clock)
        {
            _claimsTransformation = claimsTransformation;
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            var result = await base.HandleAuthenticateAsync();
            if (result.Succeeded && result.Principal != null)
            {
                var transformedPrincipal = await _claimsTransformation.TransformAsync(result.Principal);
                return AuthenticateResult.Success(new AuthenticationTicket(transformedPrincipal, result.Ticket.Properties, result.Ticket.AuthenticationScheme));
            }
            return result;
        }
    }

    public class CustomClaimsTransformer : IClaimsTransformation
    {
        public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
            var customPrincipal = new CustomClaimsPrincipal(principal);
            return Task.FromResult(customPrincipal as ClaimsPrincipal);
        }
    }

    public class CustomClaimsPrincipal : ClaimsPrincipal
    {
        public CustomClaimsPrincipal(ClaimsPrincipal principal) : base(principal)
        {
        }
        // Your custom logic for IsInRole, etc.
        public override bool IsInRole(string role)
        {
            // Your custom logic here.
            return base.IsInRole(role);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.