在ASP.NET Core MVC 1.1中,我们具有基于路径的身份验证,如下所示:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// /api/* path
app.UseWhen(ctx => IsApiRequest(ctx), subBranch =>
{
subBranch.UseApiAuth(GetApiAuthOptions());
});
// else
app.UseWhen(ctx => !IsApiRequest(ctx), subBranch =>
{
subBranch.UseOpenIdConnectAuthentication(GetOpenIdOptions());
});
}
现在,我们希望将其迁移到ASP.NET Core MVC 2.0。在新版本中,身份验证已完全进行了重新设计,而在文档中,我没有找到执行此操作的任何线索。任何想法如何迁移上面的代码?
经过2天的测试和尝试,我提出了可行的解决方案。
主要问题是,在ASP.NET Core MVC 2.0中,身份验证方法被注册为服务而不是中间件。这意味着它们必须在ConfigureServices
method中而不是in中注册Configure
,因此无法在注册时进行分支以创建分支。此外,身份验证系统用于AuthenticationOptions
确定将使用哪种身份验证方法。从我的测试中发现,该AuthenticationOptions
实例在请求之间共享,因此无法对其进行修改以进行调整DefaultScheme
。经过一番挖掘,我发现了IAuthenticationSchemeProvider
,可以将其重写以克服这些问题。
这是代码:
// Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
[...]
// Override default IAuthenticationSchemeProvider implementation
services.AddSingleton<IAuthenticationSchemeProvider, CustomAuthenticationSchemeProvider>();
// Register OpenId Authentication services
services.AddAuthentication().AddCookie(this.GetCookieOptions);
services.AddAuthentication().AddOpenIdConnect(this.GetOpenIdOptions);
// Register HMac Authentication services (for API)
services.AddAuthentication().AddHMac(this.GetHMacOptions);
[...]
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
[...]
// /api/* path
app.UseWhen(ctx => IsApiRequest(ctx), subBranch =>
{
// Register middleware which will override DefaultScheme; must be called before UseAuthentication()
subBranch.UseAuthenticationOverride(HMacAuthenticationDefaults.AuthenticationScheme);
subBranch.UseAuthentication();
});
// else
app.UseWhen(ctx => !IsApiRequest(ctx), subBranch =>
{
// Register middleware which will override DefaultScheme and DefaultChallengeScheme; must be called before UseAuthentication()
subBranch.UseAuthenticationOverride(new AuthenticationOptions
{
DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme,
DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme
});
subBranch.UseAuthentication();
});
[...]
}
中间件:
// AuthenticationOverrideMiddleware.cs
// Adds overriden AuthenticationOptions to HttpContext to be used by CustomAuthenticationSchemeProvider
public class AuthenticationOverrideMiddleware
{
private readonly RequestDelegate _next;
private readonly AuthenticationOptions _authenticationOptionsOverride;
public AuthenticationOverrideMiddleware(RequestDelegate next, AuthenticationOptions authenticationOptionsOverride)
{
this._next = next;
this._authenticationOptionsOverride = authenticationOptionsOverride;
}
public async Task Invoke(HttpContext context)
{
// Add overriden options to HttpContext
context.Features.Set(this._authenticationOptionsOverride);
await this._next(context);
}
}
public static class AuthenticationOverrideMiddlewareExtensions
{
public static IApplicationBuilder UseAuthenticationOverride(this IApplicationBuilder app, string defaultScheme)
{
return app.UseMiddleware<AuthenticationOverrideMiddleware>(new AuthenticationOptions { DefaultScheme = defaultScheme });
}
public static IApplicationBuilder UseAuthenticationOverride(this IApplicationBuilder app, AuthenticationOptions authenticationOptionsOverride)
{
return app.UseMiddleware<AuthenticationOverrideMiddleware>(authenticationOptionsOverride);
}
}
CustomAuthenticationSchemeProvider:
// CustomAuthenticationSchemeProvider.cs
// When asked for Default*Scheme, will check in HttpContext for overriden options, and return appropriate schema name
public class CustomAuthenticationSchemeProvider : AuthenticationSchemeProvider
{
private readonly IHttpContextAccessor _contextAccessor;
public CustomAuthenticationSchemeProvider(IOptions<AuthenticationOptions> options, IHttpContextAccessor contextAccessor) : base(options)
{
this._contextAccessor = contextAccessor;
}
// Retrieves overridden options from HttpContext
private AuthenticationOptions GetOverrideOptions()
{
HttpContext context = this._contextAccessor.HttpContext;
return context?.Features.Get<AuthenticationOptions>();
}
public override Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync()
{
AuthenticationOptions overrideOptions = this.GetOverrideOptions();
string overridenScheme = overrideOptions?.DefaultAuthenticateScheme ?? overrideOptions?.DefaultScheme;
if (overridenScheme != null)
return this.GetSchemeAsync(overridenScheme);
return base.GetDefaultAuthenticateSchemeAsync();
}
public override Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync()
{
AuthenticationOptions overrideOptions = this.GetOverrideOptions();
string overridenScheme = overrideOptions?.DefaultChallengeScheme ?? overrideOptions?.DefaultScheme;
if (overridenScheme != null)
return this.GetSchemeAsync(overridenScheme);
return base.GetDefaultChallengeSchemeAsync();
}
public override Task<AuthenticationScheme> GetDefaultForbidSchemeAsync()
{
AuthenticationOptions overrideOptions = this.GetOverrideOptions();
string overridenScheme = overrideOptions?.DefaultForbidScheme ?? overrideOptions?.DefaultScheme;
if (overridenScheme != null)
return this.GetSchemeAsync(overridenScheme);
return base.GetDefaultForbidSchemeAsync();
}
public override Task<AuthenticationScheme> GetDefaultSignInSchemeAsync()
{
AuthenticationOptions overrideOptions = this.GetOverrideOptions();
string overridenScheme = overrideOptions?.DefaultSignInScheme ?? overrideOptions?.DefaultScheme;
if (overridenScheme != null)
return this.GetSchemeAsync(overridenScheme);
return base.GetDefaultSignInSchemeAsync();
}
public override Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync()
{
AuthenticationOptions overrideOptions = this.GetOverrideOptions();
string overridenScheme = overrideOptions?.DefaultSignOutScheme ?? overrideOptions?.DefaultScheme;
if (overridenScheme != null)
return this.GetSchemeAsync(overridenScheme);
return base.GetDefaultSignOutSchemeAsync();
}
}
如果有人知道更好的解决方案,我很乐意看到它。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句