代码之家  ›  专栏  ›  技术社区  ›  Krzysztof

ASP中基于路径的身份验证。NET Core MVC 2.0

  •  2
  • Krzysztof  · 技术社区  · 9 年前

    在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。在新版本中,身份验证被完全重新设计,而在文档中,我找不到任何关于如何做到这一点的线索。 你知道如何迁移上面的代码吗?

    1 回复  |  直到 8 年前
        1
  •  4
  •   Krzysztof    9 年前

    经过两天的测试和尝试,我找到了可行的解决方案。

    主要问题是,在ASP。NET核心MVC 2.0中,身份验证方法注册为服务,而不是中间件。 这意味着他们必须在 ConfigureServices 方法而不是 Configure ,因此没有办法在注册时创建分支。 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.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();
        }
    }
    

    如果有人知道更好的解决方案,我很乐意看到。