How to bypass authentication middleware when not needed in ASP.NET Core

Nes*_*tor 8 c# asp.net-core

I have the following authentication handler:

public class CustomAuthenticationHandler : AuthenticationHandler<CustomAuthenticationSchemeOptions>
{
    public CustomAuthenticationHandler (
        IOptionsMonitor<CustomAuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var data = await GetDataFromDatabase(); //heavy load
        if(data.IsAuth)
        {
          var identity = new ClaimsIdentity(claims, Scheme.Name);
          var principal = new ClaimsPrincipal(identity);
          var ticket = new AuthenticationTicket(principal, Scheme.Name);

          return Task.FromResult(AuthenticateResult.Success(ticket));
       }
       return Task.FromResult(AuthenticateResult.Failed("Not authenticated"));
    }
}
Run Code Online (Sandbox Code Playgroud)

I register it like this in ConfigureServices:

services.AddAuthentication(options =>
    {
      options.DefaultAuthenticateScheme = CustomAuthenticationSchemeOptions.SchemeName;
      options.DefaultChallengeScheme = CustomAuthenticationSchemeOptions.SchemeName;
    })
    .AddScheme<CustomAuthenticationSchemeOptions, CustomAuthenticationHandler>(CustomAuthenticationSchemeOptions.SchemeName, null);
Run Code Online (Sandbox Code Playgroud)

Then I use it like this in Configure:

app.UseAuthentication();
Run Code Online (Sandbox Code Playgroud)

I have several Controllers with several Actions. Some of those actions should be accessed only after authentication.

I thought that if I use the [Authorize] attribute, I make sure that only authenticated users can access it, so my middleware will be called with that request and the authentication protocol is executed (I guess it would be a very elegant and efficient solution).

public class RegisterController: ControllerBase
{
   public async Task<AsyncResult<int>>> Reg(string name)
   {
     //...
   }
}

[Authorize]
public class DataController: Controller
{
   public async Task<AsyncResult<Data>>> GetData(int dataId)
   {
     //...
   }
}
Run Code Online (Sandbox Code Playgroud)

It seems that I was wrong, as each middleware is called each time when a request arrives.

So if I don't want to search the database after each Action request, I have to filter when the middleware is used.

I see some condition-based solution to use app.UseWhen and test the request path and other cumbersome ways.

Is there a more elegant and efficient way? As I have many actions, I can't create path checking for every one of them.

Lee*_*Lee 5

如果您将授权添加到您的中间件管道,这将是对您的 API 的所有调用的默认设置。因此,所有调用都将表现为[Authorize]应用了该属性。

这通常是可取的,因为这意味着默认情况下您的应用程序是安全的,并且您不会意外忘记 [Authorize] 属性。我建议保持这样,只需将[AllowAnonymous]标签添加到您想要公开的控制器或控制器操作。

如果您想始终明确,您只需删除app.UseAuthentication();您仍然可以使用[Authorize]它,因为您添加了要使用的服务,这将触发您的中间件。但它不会自动触发所有调用。

额外的:

为了使用授权而不必为每次调用指定方案,您可以将方案设置为默认授权策略。

services.AddAuthorization(options =>
{
    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
        CustomAuthenticationSchemeOptions.SchemeName);

    defaultAuthorizationPolicyBuilder = 
        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();

    options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
Run Code Online (Sandbox Code Playgroud)

为了扩展其他人所说的内容,身份验证和授权之间存在细微差别。身份验证说明用户是谁,授权说明他们可以做什么。以上所有只是说...只要我知道用户是谁(已通过身份验证),他们就可以使用我的操作(已授权)。因此,如果用户成功通过身份验证,则您的默认授权策略是有效的。

  • 我们就快到了:) 如果我删除 `app.UseAuthentication();` 并调用 `GetData` 方法,我的中间件就不会被执行。如果我显式添加方案名称(如“[Authorize(AuthenticationSchemes = "CustomAuthScheme")]”),则会调用它。我可以以某种方式强制管道使用我的方案作为默认方案吗? (2认同)