繁体   English   中英

ASP.NET Core 6.0 中的 IAntiforgery 存在问题

[英]Having issues with IAntiforgery in ASP.NET Core 6.0

请在最后查看我的详细回答,了解我是如何解决这个问题的。 我太沮丧了,又过了一天,有了新的视角和更多的睡眠,我找到了解决办法。

我在 5.0 中执行此操作,在Startup.Configure方法中没有问题。

基本上,我为受保护路由上的请求创建了一个 header。 我使用 React 作为前端。 当我将所有内容放入Program.cs时,我发现依赖注入、授权无法正常工作,因此我将其拆分为单独的ProgramStartup文件。

但我不能像在 5.0 中那样在 6.0 中使用以下签名:

在 5.0 中工作的示例:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
{
    app.UseEndpoints(endpoints =>            
        {
            endpoints.MapGet("antiforgery/token", context =>
            {
                var tokens = antiforgery.GetAndStoreTokens(context);
                context.Response.Headers.Append("XYZ", tokens.RequestToken!);                    
                return Task.FromResult(StatusCodes.Status200OK);
            });                
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });
}

Program.cs(我尝试拆分程序和启动 - 6.0)

var startup = new dolpassword.Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);

var app = builder.Build();

startup.Configure(app,app.Environment);

在微软网站上看到了这个例子:

app.UseRouting();

app.UseAuthorization();
// app.Services syntax error in Configure for 6.0
var antiforgery = **app.Services.GetRequiredService<IAntiforgery>();**

 app.Use((context, next) =>
 {
      var requestPath = context.Request.Path.Value;

      if (string.Equals(requestPath, "/", 
            StringComparison.OrdinalIgnoreCase)
            || string.Equals(requestPath, "/index.html", 
              StringComparison.OrdinalIgnoreCase))
        {
            var tokenSet = antiforgery.GetAndStoreTokens(context);
             context.Response.Cookies.Append("XSRF-TOKEN", 
              tokenSet.RequestToken!,
        new CookieOptions { HttpOnly = false });
         }

         return next(context);
   });

我能够在 6.0 中成功地做到这一点,所以我将分享一些代码以及我是如何解决它的。 我还使用基于策略的授权进行了 Windows 身份验证。 我将所有身份验证/授权连接放在这篇文章中的原因是因为整个解决方案依赖于身份验证、授权和防伪。

首先,我设置了我的服务。 我默认通过添加 ControllersWithViews 获得 IAntiforgery,但我想使用我自己的 header 名称,即 X-XSRF-TOKEN 而不是 .AspNet.Antiforgery.xxxx 或任何默认值。 我还需要 options.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme; 让 Windows 身份验证工作。

string CorsPolicy = "CorsPolicy";
//===================================formerly Configure Services
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
ConfigurationManager _configuration = builder.Configuration;
// Add services to the container.
**IServiceCollection? services = builder.Services;
services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; 
options.Cookie.HttpOnly = false; });**
services.AddTransient<IActiveDirectoryUserService, ActiveDirectoryUserService>();
services.AddControllersWithViews();
services.AddAuthentication(options => {//needed for Windows authentication
    options.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme;
});

添加更多...我正在使用 Windows 身份验证,因此我正在使用协商提供程序。 然后我设置了我的授权。 我插入我自己的授权策略并添加我的声明转换器以使经过身份验证的用户进入声明。 授权中的回退策略导致身份验证异常。

services.AddAuthorization(options =>
{
    // options.FallbackPolicy = options.DefaultPolicy;//authorization bombs if you include this line
    options.AddPolicy("AuthenticatedOnly", policy => {
        policy.Requirements.Add(new AuthenticatedRequirement(true));
    });    
});
services.AddTransient<IClaimsTransformation, MyClaimsTransformer>();            
services.AddTransient<IAuthorizationHandler, AppUserRoleHandler>();
services.AddTransient<IAuthorizationHandler, AuthenticatedRoleHandler>();
services.AddCors(options =>
{
    options.AddPolicy(CorsPolicy,
    builder => builder
        .WithOrigins("https://localhost:7021","https://localhost:44414") 
         //Note:  The URL must be specified without a trailing slash (/).
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials());
 });

现在我在中间件领域......如您所知,中间件中的顺序很重要。 在 5.0 中,您可以将 IAntiforgery 添加到您的构造函数中,DI 将处理 rest。 在 program.cs 中你没有那么奢侈。 幸运的是,您可以从服务集合中获取它,您可以在以下代码中看到它。

//==============formerly Startup.Configure=====================
WebApplication app = builder.Build();

app.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseCors(CorsPolicy);

IAntiforgery? antiforgery = app.Services.GetRequiredService<IAntiforgery>();

现在,当我设置端点路由时。 发现 UseRouting 和 Use.Endpoints 已成婚,需要配对。

我还创建了一个受保护的路由“/auth”(受我的授权策略保护)来获取我们在服务集合中添加它时生成的防伪请求令牌。 所以这个 header 不会像 cookie 那样在请求之间持久化。 最小的 API 允许我在不创建 controller 的情况下创建路线,并在单独的 controller ZA2F2A198EBC2AB6BBB4C2 中执行操作。

app.UseEndpoints(endpoints =>          
{
    endpoints.MapGet("/auth", context =>
    {        
        var tokens = antiforgery.GetAndStoreTokens(context);
        context.Response.Headers.Append("XYZ", tokens.RequestToken!);       
        return Task.FromResult(Results.Ok());
    }).RequireAuthorization("AuthenticatedOnly");

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

我的 React 前端将使用 fetch get 请求从 headers 集合中获取令牌,然后坚持到第二个 post 请求,瞧,它可以工作了。

顺便说一句,React 没有像 Angular 那样提供开箱即用的防伪功能,这与它的极简主义 API 精神保持一致。

我要发布的操作如下所示:

[HttpPost]
[Authorize(Policy="AuthenticatedOnly")]
[ValidateAntiForgeryToken]
public string Update()

我完全意识到还有其他方法可以做到这一点。

暂无
暂无

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

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