繁体   English   中英

通过 ASP.NET 内核上的身份验证保护 Static 文件

[英]Protect Static Files with Authentication on ASP.NET Core

在我之前的问题中,我问了一个通用问题,如何为 static 内容添加权限。 在这里,我想更精确。

在我的项目中,我在wwwroot下添加了一个文件夹以简化代码,我在其中保存了我想要保护的 html 文件。 此文件夹称为infographics

在此处输入图像描述

每个文件的属性是:

  • 构建操作:内容
  • 复制到Output目录:不要复制

在此处输入图像描述

按照Microsoft 文档中的说明,我更改了Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.Configure<IdentityServerConfiguration>(Configuration.GetSection("IdentityServerConfiguration"));

        services.AddDistributedMemoryCache();

        services.AddSession(options =>
        {
            options.Cookie.Name = ".my.Session";
            options.IdleTimeout = TimeSpan.FromHours(12);
        });

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie(options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
            options.Cookie.Name = "my.dashboard";
        })
        .AddOpenIdConnect("oidc", options =>
        {
            IdentityServerConfiguration idsrv = Configuration.GetSection("IdentityServerConfiguration")
                                                .Get<IdentityServerConfiguration>();
            options.Authority = idsrv.Url;
            options.ClientId = idsrv.ClientId;
            options.ClientSecret = idsrv.ClientSecret;

            #if DEBUG
            options.RequireHttpsMetadata = false;
            #else
            options.RequireHttpsMetadata = true;
            #endif

            options.ResponseType = "code";

            options.Scope.Clear();
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
            options.Scope.Add("roles");
            options.Scope.Add("offline_access");

            options.ClaimActions.MapJsonKey("role", "role", "role");

            options.GetClaimsFromUserInfoEndpoint = true;
            options.SaveTokens = true;

            options.SignedOutRedirectUri = "/";

            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role,
            };
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }
        app.UseHttpsRedirection();

        app.UseAuthentication();

        app.UseStaticFiles(new StaticFileOptions
        {
            OnPrepareResponse = ctx =>
            {
                if (ctx.Context.Request.Path.StartsWithSegments("/infographics"))
                {
                    ctx.Context.Response.Headers.Add("Cache-Control", "no-store");

                    if (!ctx.Context.User.Identity.IsAuthenticated)
                    {
                        // respond HTTP 401 Unauthorized with empty body.
                        ctx.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        ctx.Context.Response.ContentLength = 0;
                        ctx.Context.Response.Body = Stream.Null;

                        // - or, redirect to another page. -
                        // ctx.Context.Response.Redirect("/");
                    }
                }
            }
        });

        app.UseRouting();

        app.UseAuthorization();

        app.UseCookiePolicy();
        app.UseSession();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

我期望的是,当用户请求/infographics时, OnPrepareResponse会验证请求,并且如果用户通过身份验证,则会看到该页面。 但是,经过大量代码更改后,结果总是相同的(在我的本地机器上):

找不到此本地主机页面

我尝试将此代码添加到 map 文件夹html作为infographics ,但没有成功。

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.ContentRootPath, "html")),
    RequestPath = "/infographics"
});

有任何想法吗?

更新

不适用于 HTML 文件。 我认为问题来自 HTML 文件,因为它们是 ASP.NET 的 static 内容。

我在OnPrepareResponse上放置了一个断点并调用页面infographics/index.html 显示页面(红色箭头),然后应用程序在断点处停止(蓝色箭头)。

在此处输入图像描述

我建议将该文件夹放在 wwwroot 文件夹之外,这将删除 static 文件对其的访问权限。

然后,您可以像这样从 controller 提供文件:

public IActionResult GetInfographic(string name)
{
    var infographicPath = resolvePath(name);
    return new FileStreamResult(new FileStream(infographicPath, FileMode.Open, FileAccess.Read), mime);
}

鉴于这是 controller,您可以使用[Authorize]标签以任何您喜欢的方式保护它。

对于任何其他最终在这里搜索保护安全 static 文件的人。

对于 .NET Core 5。文档已更新以包括如何处理此问题

根据文档:

根据授权提供 static 文件:

  1. 将它们存储在 wwwroot 之外。
  2. 在调用 UseAuthorization 之后调用 UseStaticFiles,指定路径。
  3. 设置回退授权策略。

确保也添加一个后备策略:

    services.AddAuthorization(options =>
    {
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
    });

见: Static文件授权

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM