Asp .net Core 3.1 transforming ClaimsIdentity with multiple AuthenticationScheme
在ASP .Net Core应用程序中实现几个授权方案时,我遇到了一个问题。可以说在Startup.cs
中这样声明它们
1 2 3 4 5 6 7 8 | public void ConfigureServices(IServiceCollection services) { AuthenticationBuilder builder = services .AddAuthentication() .AddBasicAuthentication(o => { o.Realm ="MyRealm"; }) .AddApiKeyAuthentication() .AddBearerToken(opt => { }); } |
这些方案中的每一个都提供自己的AuthenticationHandler实现,如果成功,则返回ClaimsIdentity。但是在每种情况下,声明的结构都是不相关的,即ApiKeyAuthentication可能会返回带有存储在声明" api_service"中的业务敏感数据的ClaimsIdentity,而BearerTokenScheme会将其存储在声明" sub"中,而我对此没有控制权。因此,如果我想在控制器中使用此信息来将某个进程与某个服务(称为api方法)相关联,则必须实现一些复杂的逻辑来分析当前的ClaimsIdentity,其auth方案和声明集。
相反,我想实现ClaimsIdentity到MyServiceClaimsIdentity的某种转换,这将以一种便捷的方式公开声明,因此我可以在我的Controllers代码中轻松地使用它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class MyServiceClaimsIdentity: IIdentity { private readonly ClaimsIdentity innerIdentity; public Guid? UserId {get; } public string UserName {get; } public string ServiceName {get; } public MyServiceClaimsIdentity(ClaimsIdentity identity) { this.innerIdentity = identity; TransformClaimsIntoProperties(); } private void TransformClaimsIntoProperties() { ...... } } |
我尝试实现某种"转换式" AuthenticationHandler,该认证处理程序将在所有其他处理程序产生其ClaimsIdentity之后产生MyServiceClaimsIdentity。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class FinalAuthenticationHandler : AuthenticationHandler<FinalAuthenticationOptions> { public FinalAuthenticationHandler( IOptionsMonitor<FinalAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { if (!this.Context.User.Identity.IsAuthenticated) { return null; } var identity = new MyServiceClaimsIdentity(this.Context.User.Identity); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, this.Scheme.Name); return AuthenticateResult.Success(ticket); } } |
在这一点上太糟糕了this.Context.User.Identity没有用户的任何信息,所以我很困惑在何处放置此转换逻辑,或者我将如何在FinalAuthenticationHandler中获得其他Handler提供的当前ClaimsIdentity。任何帮助将不胜感激。
实现IClaimsTransformation并将其注册为Singleton可以很好地完成工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | internal sealed class ClaimsTransformation : IClaimsTransformation { private readonly IDictionary<string, IClaimsHandler> handlersMap; public ClaimsTransformation(IEnumerable<IClaimsHandler> handlers) { if (handlers == null) { throw new ArgumentNullException(nameof(handlers)); } this.handlersMap = handlers.ToDictionary(t => t.SchemeName); } public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) { if (!(principal.Identity is ClaimsIdentity claimsIdentity)) { throw new InvalidOperationException($"Principal.Identity is of type {principal.Identity.GetType()}, expected ClaimsIdentity"); } if (!this.handlersMap.TryGetValue(principal.Identity.AuthenticationType, out var handler)) { throw new AuthenticationException($"Scheme of type {principal.Identity.AuthenticationType} is not supported"); } var result = new ClaimsPrincipal(handler.Handle(claimsIdentity)); return Task.FromResult(result); } } |