【问题标题】:Protect Static Files with Authentication on ASP.NET Core在 ASP.NET Core 上使用身份验证保护静态文件
【发布时间】:2021-08-09 23:05:48
【问题描述】:

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

在我的项目中,我在wwwroot 下添加了一个文件夹来简化代码,我在其中保存了我想要保护的 html 文件。这个文件夹叫做infographics

每个文件的属性是:

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

按照Microsoft documentation的指示,我更改了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 会验证请求,并且如果用户通过身份验证,则会看到该页面。但是,经过大量代码更改后,结果总是相同的(在我的本地机器上):

找不到这个本地主机页面

我尝试添加此代码以将项目根目录中的文件夹html 映射为infographics,但没有成功。

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

有什么想法吗?

更新

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

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

【问题讨论】:

  • 如果您在 OnPrepareResponse 中的请求路径检查中放置断点,它会命中吗? wwwroot 文件夹中的其他文件是否可以访问?
  • 不,应用程序没有到达OnPrepareResponse,我不明白为什么。我尝试使用 ASP.NET Core MVC 中的应用程序,我可以访问 OnPrepareResponse 并且该功能正在运行:问题是 wwwroot 中的所有静态文件都受到保护,例如 CSS 未应用。

标签: c# asp.net-core


【解决方案1】:

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

然后您可以像这样从控制器提供文件:

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

鉴于这是一个控制器,您可以使用[Authorize] 标签来保护它。

【讨论】:

    【解决方案2】:

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

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

    根据文档:

    根据授权提供静态文件:

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

    确保也添加后备策略:

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

    见:Static file authorization

    【讨论】:

      猜你喜欢
      • 2018-01-25
      • 2011-02-23
      • 1970-01-01
      • 2018-12-07
      • 1970-01-01
      • 1970-01-01
      • 2021-01-17
      • 2021-09-24
      • 2019-01-04
      相关资源
      最近更新 更多