【问题标题】:ASP.NET Core disable authentication in development environmentASP.NET Core 在开发环境中禁用身份验证
【发布时间】:2017-04-28 00:14:44
【问题描述】:

是否可以在 ASP.NET Core 应用程序中“禁用”身份验证而不更改其逻辑?

我有一个使用外部身份服务器应用程序进行身份验证的 .net 网站。 无论如何,我希望能够在开发时模拟身份验证(ASPNETCORE_ENVIRONMENT = Development),广播访问所有操作而忽略授权属性。

是否可以只模拟服务集合中的一些服务?

【问题讨论】:

    标签: asp.net-core authentication


    【解决方案1】:

    更新到 net core 3.1 后,mvc AllowAnonymousFilter 不再为我们工作。我们发现有条件地添加自定义 IAuthorizationHander 是有条件地绕过身份验证的最简单方法。

    例如。

    /// <summary>
    /// This authorisation handler will bypass all requirements
    /// </summary>
    public class AllowAnonymous : IAuthorizationHandler
    {
        public Task HandleAsync(AuthorizationHandlerContext context)
        {
            foreach (IAuthorizationRequirement requirement in context.PendingRequirements.ToList())
                context.Succeed(requirement); //Simply pass all requirements
            
            return Task.CompletedTask;
        }
    }
    

    然后在Startup.ConfigureServices有条件地注册这个handler。

    private readonly IWebHostEnvironment _env;
    public Startup(IWebHostEnvironment env)
    {
        _env = env;
    }
    
    public void ConfigureServices(IServiceCollection services)
    {
      {...}
    
      //Allows auth to be bypassed
      if (_env.IsDevelopment())
        services.AddSingleton<IAuthorizationHandler, AllowAnonymous>();
    }
    
    

    注意 AddAuthenticationAddAuthorization 服务仍然按照产品代码注册和配置(这很好)。

    为了让我们的单元测试绕过身份验证,我们添加了一个新的匿名测试库,其中包含一个启动类,该类在没有任何条件的情况下添加了这一行。又好又简单!

    【讨论】:

    • Nice :) .Net Core 3.0 IWebHostEnvironment 没有 IsLocalDev() 方法,但它确实有 IsDevelopment() -- 我个人只是使​​用 #if (DEBUG) 指令虽然
    • 是的,这是我们的扩展方法。例如:public static bool IsLocalDev(this IWebHostEnvironment environment) =&gt; environment.IsEnvironment("LocalDev");
    • 这绝对有助于 3.1 升级。不确定这是否是一个已知问题?
    • ozzy 的 IPolicyEvaluator 解决方案是一种更好、更简洁的方法。
    • 不需要使用Linq,在context.PendingRequirements上调用.ToList(),因为它已经是IEnumerable了,不需要转换就可以被foreach迭代。
    【解决方案2】:

    在 ASP.NET Core 3.x 及更高版本中,您可以通过使用 WithMetadata 扩展方法将 AllowAnonymousAttribute 应用到您的端点来绕过开发环境中的授权。


    示例 1.AllowAnonymousAttribute 应用于 ASP.NET Core 6 中 Program.cs 中的控制器

    if (app.Environment.IsDevelopment())
        app.MapControllers().WithMetadata(new AllowAnonymousAttribute());
    else
        app.MapControllers();
    

    ...或在 ASP.NET Core 3.0 及更高版本中的 Startup.Configure()

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        //...
        app.UseEndpoints(endpoints =>
        {
            if (env.IsDevelopment())
                endpoints.MapControllers().WithMetadata(new AllowAnonymousAttribute());
            else
                endpoints.MapControllers();
        });
    }
    

    请注意,这会将AllowAnonymousAttribute 应用于所有控制器。


    示例 2.AllowAnonymousAttribute 应用于 ASP.NET Core 6 中的最小 API 端点

    var hiEndpoint = app
        .MapGet("/hi", () => "Hello!")
        .WithMetadata(new AuthorizeAttribute());
    
    if (app.Environment.IsDevelopment())
        hiEndpoint.WithMetadata(new AllowAnonymousAttribute());
    

    详情

    以上示例中的endpointsapp 都实现了IEndpointRouteBuilder,它具有多个Map 扩展方法,如MapControllers()MapGet(...),它们返回IEndpointConventionBuilder

    WithMetadataIEndpointConventionBuilder 的扩展,可以根据Map 方法的结果调用。

    AllowAnonymousAttribute 来自文档的描述:

    指定应用该属性的类或方法不需要授权。

    【讨论】:

    • 谢谢,这就是我一直在寻找的:禁用 .NET Core 3.x 和 WebApi 开发环境的身份验证。所有其他解决方案都不适合我。
    • 适用于截至 9 月 20 日的最新 asp.net 核心
    • 我更喜欢这个解决方案,因为它利用了 Dotnetcore 管道
    • 我得到一个错误,IApplicationBuilder 没有Environment 的定义()我将它注入到配置中,但它在 asp core 6.0.1 中没有MapControllers 我没有能够克服这个
    • @Transformer,这是正确的。在第一个 ASP.NET Core 6 示例中,app 的类型为 WebApplication 而不是 IApplicationBuilder。请使用dotnet new webapi.NET 6 CLI 命令检查类型。
    【解决方案3】:

    您可能要考虑的另一个解决方案是使用 IPolicyEvaluator。这意味着您可以保留所有现有的安全元素。

    public class DisableAuthenticationPolicyEvaluator : IPolicyEvaluator
    {
        public async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
        {
            // Always pass authentication.
            var authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(), new AuthenticationProperties(), JwtBearerDefaults.AuthenticationScheme);
            return await Task.FromResult(AuthenticateResult.Success(authenticationTicket));
        }
    
        public async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
        {
            // Always pass authorization
            return await Task.FromResult(PolicyAuthorizationResult.Success());
        }
    }
    

    在 Startup.cs 中,确保它出现在 ConfigureServices 方法的顶部。例如。

        public void ConfigureServices(IServiceCollection services)
        {
            if (env.IsDevelopment())
            {
                // Disable authentication and authorization.
                services.TryAddSingleton<IPolicyEvaluator, DisableAuthenticationPolicyEvaluator>();
            }
            ...
    

    如果您使用的是 Core 3.1 并且希望使用 WebApplicationFactory 而不是 Startup.cs(感谢下面的 cmets),您可以执行以下操作:

    public class MyWebApplicationFactory : WebApplicationFactory<Program>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureTestServices(services =>
            {
                // Disable Authentication.
                services.RemoveAll<IPolicyEvaluator>();
                services.AddSingleton<IPolicyEvaluator, DisableAuthenticationPolicyEvaluator>();
            });
        }
    }
    

    【讨论】:

    • 最佳解决方案,适用于核心 3.1。简单、优雅、可重复使用。
    • 我有另一个 IPolicyEvaluator 由 prod Startup 注入(使用 JWT)所以我需要在 AddSingleton 之前 services.RemoveAll&lt;IPolicyEvaluator&gt;(); .. 不使用 TryAddSingleton 是少一个导入,无需尝试,正如我发现的那样,TryAddSingleton 确实不会添加单例如果已经有一个注册
    • 也适用于核心 5
    • 确实很好的解决方案!
    【解决方案4】:

    我在illucIT Blog 上找到了解决此问题的方法。

    此代码必须有效:

    if (env.IsDevelopment()) {
       services.AddMvc(opts =>
       {
          opts.Filters.Add(new AllowAnonymousFilter());
       });
    } else {
       services.AddMvc();
    }
    

    【讨论】:

    • 这在最新的 .net 核心中不起作用,因为 IHostingEnvironment 没有传递给 ConfigureServices
    • @Dagrooms 您可以将IHostingEnvironment 注入Startup 构造函数并在ConfigureServices 中使用它;)
    • 是的,在浏览文档后发现,很难找到好的信息
    【解决方案5】:

    在没有更多细节的情况下给出详细答案很棘手,但我之前通过有条件注册实现了这一点:

    • 外部身份验证中间件
    • 需要经过身份验证的请求的全局策略

    它看起来像:

    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            Environment = env;
        }
    
        public IHostingEnvironment Environment { get; }
    
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(x =>
            {
                if (!Environment.IsDevelopment())
                {
                    var authenticatedUserPolicy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .Build();
    
                    x.Filters.Add(new AuthorizeFilter(authenticatedUserPolicy));
                }
            });
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseStaticFiles();
    
            if (!Environment.IsDevelopment())
            {
                // Register external authentication middleware
            }
    
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    

    在我的例子中,授权过滤器是全局应用的,因此 MVC 应用程序的每一个操作都需要经过身份验证的用户。

    如果您有不同的要求 - 某些操作上的细粒度 [Authorize] 属性 - 那么您可以通过更改相关授权策略的构建方式来获得相同的结果。它们基本上可以不包含任何要求。

    AuthorizationPolicy yourCustomPolicy = null;
    if (Environment.IsDevelopment())
    {
        yourCustomPolicy = new AuthorizationPolicyBuilder().Build();
    }
    else
    {
        yourCustomPolicy = new AuthorizationPolicyBuilder()
            // chaining appropriate methods to suit your needs
            .Build();
    }
    

    【讨论】:

    • 它看起来正是我正在寻找的东西。我来测试一下
    • 此解决方案可能适用于 ASP.NET Core 1.x,但在 ASP.NET Core 2.0 中,他们更改了身份验证管道。在 2.0 中对我有用的是这个答案 stackoverflow.com/a/40156927/1118893
    • 你能告诉我你认为什么不起作用吗?我在 ASP.NET Core 2.0 中成功使用了这种技术。
    【解决方案6】:

    这是为了澄清@Kirill Lutsenko 对他在IllucIT blog post 上找到的方法的回答(请注意,在我的情况下,这是针对.NET Core 2.0。我看到其他答案说AllowAnonymousFilter 方法不起作用.NET Core 3.1):

    Startup 类有一个重载的构造函数。其中一个重载采用IHostingEnvironment 参数。你需要使用这个版本的构造函数。

    在 Startup 类中创建IHostingEnvironment 类型的属性。称之为Environment。然后在构造函数中设置该属性。

    然后,在ConfigureServices 方法中,您可以使用Environment.IsDevelopment()

    public class Startup
    {        
        public Startup(IHostingEnvironment environment)
        {
            Environment = environment;
        }
        
        public IHostingEnvironment Environment { get; }
    
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            //...
    
            services.AddMvc(options =>
            {
                // This uses the Environment property populated in the constructor.
                if (Environment.IsDevelopment())
                {
                    options.Filters.Add(new AllowAnonymousFilter());
                }
                
                // Set other options here.  For example:
                options.ModelBinderProviders.Insert(0, new UTCDateTimeModelBinderProvider());
                //...
            });
            
            //...
        }
    }
    

    附带说明一下,在现实生活中,我们使用了不同的构造函数重载,它将IConfiguration 对象和IHostingEnvironment 对象作为参数。这允许我们基于 appsettings.json 配置文件配置服务。

    例如:

    public class Startup
    {        
        public Startup(IConfiguration configuration, IHostingEnvironment environment)
        {
            Configuration = configuration;
            Environment = environment;
        }
        
        public IConfiguration Configuration { get; }
        public IHostingEnvironment Environment { get; }
    
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            //...
    
            // Data access via Entity Framework
            services.AddDbContext<ContainersDbContext>(options =>
            {
                options.UseNpgsql(Configuration.GetConnectionString("OrdersDatabase"));
            });
            
            //...
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-06
      • 2021-06-07
      • 2019-09-14
      • 1970-01-01
      • 1970-01-01
      • 2017-03-30
      相关资源
      最近更新 更多