简体   繁体   English

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

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

Please look at my long answer at the end about how I resolved this.请在最后查看我的详细回答,了解我是如何解决这个问题的。 I had gotten too frustrated and after another day with a fresh perspective and more sleep, I got to a solution.我太沮丧了,又过了一天,有了新的视角和更多的睡眠,我找到了解决办法。

I did this in 5.0 with no issues in the Startup.Configure method.我在 5.0 中执行此操作,在Startup.Configure方法中没有问题。

Basically I created a header for the request on a protected route.基本上,我为受保护路由上的请求创建了一个 header。 I'm using React as the front end.我使用 React 作为前端。 I'm finding when I place everything in Program.cs the dependency injection, authorization doesn't work right so I split up into separate Program and Startup files.当我将所有内容放入Program.cs时,我发现依赖注入、授权无法正常工作,因此我将其拆分为单独的ProgramStartup文件。

But I can't use the following signature in 6.0 like I did in 5.0:但我不能像在 5.0 中那样在 6.0 中使用以下签名:

example that worked in 5.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 (my attempt to split up program and startup - 6.0) Program.cs(我尝试拆分程序和启动 - 6.0)

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

var app = builder.Build();

startup.Configure(app,app.Environment);

Saw this example on Microsoft website:在微软网站上看到了这个例子:

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);
   });

I was able to successfully do this in 6.0 so I will share some of the code and how I resolved it.我能够在 6.0 中成功地做到这一点,所以我将分享一些代码以及我是如何解决它的。 I also had Windows authentication baked in with a policy-based authorization.我还使用基于策略的授权进行了 Windows 身份验证。 The reason I'm putting all the authentication/authorization wireup in this post is because the entire solution relies on authentication, authorization and antiforgery.我将所有身份验证/授权连接放在这篇文章中的原因是因为整个解决方案依赖于身份验证、授权和防伪。

First I set up my services.首先,我设置了我的服务。 I get IAntiforgery by default by adding ControllersWithViews but I want to use my own header name, which is X-XSRF-TOKEN instead of the.AspNet.Antiforgery.xxxx or whatever the default is.我默认通过添加 ControllersWithViews 获得 IAntiforgery,但我想使用我自己的 header 名称,即 X-XSRF-TOKEN 而不是 .AspNet.Antiforgery.xxxx 或任何默认值。 I also needed options.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme;我还需要 options.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme; to get Windows auth working.让 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;
});

Adding more... I'm using Windows auth so I'm using the Negotiate provider.添加更多...我正在使用 Windows 身份验证,因此我正在使用协商提供程序。 Then I set up my Authorization.然后我设置了我的授权。 I insert my own authorization policy and also add my claims transformers to get the authenticated user into a claim.我插入我自己的授权策略并添加我的声明转换器以使经过身份验证的用户进入声明。 The fallback policy in Authorization was causing an Authentication exception.授权中的回退策略导致身份验证异常。

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());
 });

Now I'm in the middleware territory...as you know, order matters in your middleware.现在我在中间件领域......如您所知,中间件中的顺序很重要。 In 5.0 you could add IAntiforgery to your constructor and DI would handle the rest.在 5.0 中,您可以将 IAntiforgery 添加到您的构造函数中,DI 将处理 rest。 In program.cs you don't have that luxury.在 program.cs 中你没有那么奢侈。 Fortunately you can just grab it out of your services collection and you see that in the following code.幸运的是,您可以从服务集合中获取它,您可以在以下代码中看到它。

//==============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>();

Now when I'm setting up my endpoint routing.现在,当我设置端点路由时。 Found out that UseRouting and Use.Endpoints are married at the hip and need to be paired.发现 UseRouting 和 Use.Endpoints 已成婚,需要配对。

I also create a protected route "/auth" (protected by my authorization policy) to grab the antiforgery request token generated when we added it in the services collection.我还创建了一个受保护的路由“/auth”(受我的授权策略保护)来获取我们在服务集合中添加它时生成的防伪请求令牌。 So this header won't be persisted from request to request like a cookie would.所以这个 header 不会像 cookie 那样在请求之间持久化。 The minimal API allows me to create a route without creating the controller and action in a separate controller class.最小的 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?}");
});

My React front end will use a fetch get request to get the token from the headers collection and then stick into a second post request and voila it works.我的 React 前端将使用 fetch get 请求从 headers 集合中获取令牌,然后坚持到第二个 post 请求,瞧,它可以工作了。

BTW, React doesn't provide Antiforgery functionality out of the box like Angular, in step with it's minimalist API ethos.顺便说一句,React 没有像 Angular 那样提供开箱即用的防伪功能,这与它的极简主义 API 精神保持一致。

The action I'm posting to looks like this:我要发布的操作如下所示:

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

I fully realize there are other ways to do this.我完全意识到还有其他方法可以做到这一点。

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

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