我的 .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
吗?
请尝试使用下面的代码,它应该可以工作。我已经在我身边尝试过。
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);
}
}
}