未指定authenticationScheme,并且未找到具有默认身份验证和自定义授权的DefaultChallengeScheme

Gal*_*ina 30 .net c# asp.net asp.net-core-2.0

我有一个net core 2.0应用程序和授权问题.我想使用具有特殊request.header和标准默认身份验证的自定义授权.首先,我在startup.cs中添加配置:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  ...
services.AddAuthorization(options =>
            {
                options.AddPolicy(DefaultAuthorizedPolicy, policy =>
                {
                    policy.Requirements.Add(new TokenAuthRequirement());
                });
            });
services.AddSingleton<IAuthorizationHandler, AuthTokenPolicy>();
  ...
}
Run Code Online (Sandbox Code Playgroud)

和AuthTokenPolicy.cs

public class AuthTokenPolicy : AuthorizationHandler<TokenAuthRequirement>
{   
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TokenAuthRequirement requirement)
    {
        var filterContext = context.Resource as AuthorizationFilterContext;
        var response = filterContext.HttpContext.Response;
        try
        {
            // some validation code

            var isValidToken = isValidTokenTask.Result;
            if (!isValidToken)
            {
                response.StatusCode = 401;
                return Task.CompletedTask;
            }

            response.StatusCode = 200;
            context.Succeed(requirement);
        }
        catch (Exception)
        {
            return Task.CompletedTask;
        }
        return Task.CompletedTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

在HomeController.cs中

[Authorize(Policy = Startup.DefaultAuthorizedPolicy)]
    public async Task<IActionResult> IsVisible()
Run Code Online (Sandbox Code Playgroud)

如果我在AuthTokenPolicy中使用错误的request.header,我会看到它.但在日志中我看到错误:

System.InvalidOperationException:未指定authenticationScheme,并且未找到DefaultChallengeScheme.\ r \n在Microsoft.AspNetCore.Authentication.AuthenticationService.d__11.MoveNext()\ r \n ---从上一个位置的堆栈跟踪结束抛出---\r \n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n,在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\ r \n在Microsoft.AspNetCore.Mvc. ChallengeResult.d__14.MoveNext()\ r \n ---从抛出异常的上一个位置开始的堆栈跟踪结束---在系统的System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n中的\ r \n .Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\ r \n在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__19.MoveNext()\ r \n- ---从先前位置抛出异常的堆栈跟踪结束---\r \n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \nn \na t System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\ r \n在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()\ r \n ---从上一个位置的堆栈跟踪结束抛出---\r \n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n,在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\ r \n在Microsoft.AspNetCore.Mvc. Internal.ResourceInvoker.d__15.MoveNext()\ r \n ---从抛出异常的上一个位置开始的堆栈跟踪结束---\r \n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\ r \n在Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()\ r \n --- ---从先前位置抛出异常的堆栈跟踪结束---\r \n在System.Runtime.CompilerSe的System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n中 在Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.d__3.MoveNext()\ r \n的rvices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\ r \n ---从抛出异常的上一个位置开始的堆栈跟踪结束---\r \n \n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\ r \n在React.AspNet.BabelFileMiddleware.d__5.MoveNext()\ r \n \n ---从抛出异常的上一个位置开始的堆栈跟踪结束---在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification的System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n中的\ r \n任务任务)\ r \n在Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.d__6.MoveNext()\ r \n ---从抛出异常的上一个位置的堆栈跟踪结束---\r \n在System.Runtime System.Runtime.CompilerServices.TaskAwaiter.Han中的.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \n dleNonSuccessAndDebuggerNotification(任务任务)\ r \n在core.common.Middleware.LoggingMiddleware.d__3.MoveNext()在D:\ Dev\microservicePDP\Template\core.common\Middleware\LoggingMiddleware.cs:第72行

阅读迁移身份验证和身份到ASP.NET Core 2.0之后,我在startup.cs中添加了此代码

文章引文:

services.AddAuthentication(options => 
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});
Run Code Online (Sandbox Code Playgroud)

如果满足下列条件之一,则在2.0中定义默认方案:您希望用户自动登录您使用[Authorize]属性或授权策略而不指定方案

我在ConfigureServices中添加了AuthenticationScheme和DefaultChallengeScheme.这没有帮助,这里也是同样的错误.我试过用过app.UseAuthentication(); 在StartUp.cs中的Configure中,没有结果.有人可以解释如何使用自定义授权而无需身份验证吗?

Gal*_*ina 18

好.正确答案是:不要使用授权而不是身份验证.我应该获得使用标头服务所有客户端的完全访问权限.工作代码是:

public class TokenAuthenticationHandler : AuthenticationHandler<TokenAuthenticationOptions> 
{
    public IServiceProvider ServiceProvider { get; set; }

    public TokenAuthenticationHandler (IOptionsMonitor<TokenAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IServiceProvider serviceProvider) 
        : base (options, logger, encoder, clock) 
    {
        ServiceProvider = serviceProvider;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync () 
    {
        var headers = Request.Headers;
        var token = "X-Auth-Token".GetHeaderOrCookieValue (Request);

        if (string.IsNullOrEmpty (token)) {
            return Task.FromResult (AuthenticateResult.Fail ("Token is null"));
        }           

        bool isValidToken = false; // check token here

        if (!isValidToken) {
            return Task.FromResult (AuthenticateResult.Fail ($"Balancer not authorize token : for token={token}"));
        }

        var claims = new [] { new Claim ("token", token) };
        var identity = new ClaimsIdentity (claims, nameof (TokenAuthenticationHandler));
        var ticket = new AuthenticationTicket (new ClaimsPrincipal (identity), this.Scheme.Name);
        return Task.FromResult (AuthenticateResult.Success (ticket));
    }
}
Run Code Online (Sandbox Code Playgroud)

Startup.cs:

#region Authentication
services.AddAuthentication (o => {
    o.DefaultScheme = SchemesNamesConst.TokenAuthenticationDefaultScheme;
})
.AddScheme<TokenAuthenticationOptions, TokenAuthenticationHandler> (SchemesNamesConst.TokenAuthenticationDefaultScheme, o => { });
#endregion
Run Code Online (Sandbox Code Playgroud)

和mycontroller.cs

[Authorize(AuthenticationSchemes = SchemesNamesConst.TokenAuthenticationDefaultScheme)]
public class MainController : BaseController
{ ...}
Run Code Online (Sandbox Code Playgroud)

我现在找不到TokenAuthenticationOptions,但它是空的.我发现了同一类PhoneNumberAuthenticationOptions:

namespace Project.Auth{
public class PhoneNumberAuthenticationOptions : AuthenticationSchemeOptions
{
    public Regex PhoneMask { get; set; }// = new Regex("7\\d{10}");

}}
Run Code Online (Sandbox Code Playgroud)

您应该定义静态类SchemesNamesConst类似于:

namespace Common.Const{
public static class SchemesNamesConst
{
    public const string TokenAuthenticationDefaultScheme = "TokenAuthenticationScheme";
}}
Run Code Online (Sandbox Code Playgroud)

  • TokenAuthenticationOptions 类来自哪里?你是自己定义的还是从图书馆定义的? (4认同)
  • SchemesNamesConst 在哪里定义的? (3认同)
  • 我不会投反对票,但请避免对您的答案做出不客观的结论,例如“这是最好的答案”或类似的内容。这不是一个论坛,对我来说它没有帮助。提前致谢。 (2认同)

ali*_*rei 9

这对我有用

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
        options =>
        {
            options.LoginPath = new PathString("/auth/login");
            options.AccessDeniedPath = new PathString("/auth/denied");
        });
Run Code Online (Sandbox Code Playgroud)


小智 7

在我尝试使用未经身份验证的 AuthorizationHandler 后,我遇到了这个问题。我试图根据策略自动验证验证码,因此我可以将该策略应用于必须使用验证码保护的所有端点。

当我想在无法验证验证码的情况下抛出 401 或 403 时,我以同样的失败告终。

我不喜欢这里的任何解决方案,因为它们很老套,当我们没有真正做任何事情时,他们尝试使用身份验证和授权,毕竟 ASP.NET Core 将授权与身份验证联系起来,因为在他们的设计原则中,你可以'如果您不知道某人是谁,请不要决定该人是否获得授权。

这让我意识到您有时可以(取决于您的要求)使用资源过滤器,而不是尝试破解授权过滤器以使其按您希望的方式工作,而使身份验证/授权系统保持独立且独立。

该解决方案可能并不适合所有人,但它可能会帮助那些像我一样不需要身份验证的人,而只需根据使用端点的人的身份以外的其他因素来决定是否可以访问控制器。

public class YourResourceFilter : IAsyncResourceFilter
{
    //any services you might need through DI (dependency injection)
    private readonly ICaptchaService captchaService;
    public YourResourceFilter(ICaptchaService captchaService)
    {
        this.captchaService = captchaService;
    }
    //this is where you need to code any logic related to your requirements.
    public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
    {
        //in my case I needed to get a header value, and verify it using my captcha service.
        //but you should be able to do almost anything here because you have access to DI.
        if (context.HttpContext.Request.Headers.TryGetValue("captcha", out var values))
        {
            var response = values.FirstOrDefault();
            if (response != null && await captchaService.VerifyCaptchAsync(response))
            {
                await next(); //if all is OK then keep going.
                return;
            }
        }
        //if not then set the status code to whatever you want. In my case I wanted to return 403 when the captcha couldn't be verified.
        context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
    }
}

Run Code Online (Sandbox Code Playgroud)

现在,我们如何在控制器上运行这个过滤器?我们如何在资源上使用依赖注入?

您需要创建一个继承 ServiceFilterAttribute 的属性,如下所示:

/// <summary>
/// This applies your own resource filter whenever you apply this attribute on a controller endpoint.
/// </summary>
public class YourResourceFilterAttribute : ServiceFilterAttribute
{
    public YourResourceFilterAttribute() : base(typeof(YourResourceFilter))
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

最后不要忘记向依赖注入容器注册您的过滤器:

builder.Services.AddSingleton<YourResourceFilter>();
Run Code Online (Sandbox Code Playgroud)

并将其应用到您想要应用此功能的任何控制器:

[AllowAnonymous] //you can add this attribute is you want to bypass any authentication/authorization set up on that Asp.net core project, so you can use it no matter what configuration you have on the authentication/authorization system
[YourResourceFilter]
[HttpPost]
public Task ExampleAsync(CancellationToken cancellationToken = default)
{
    // whatever code your controller endpoint has
}
Run Code Online (Sandbox Code Playgroud)

翻译:博士:

与上述方法相比,使用 aIAsyncResourceFilter和 a来应用自定义请求逻辑是一种更灵活、更简单的方法。ServiceFilterAttribute

  1. 实现一个IAsyncResourceFilter将您的逻辑保存在您的“授权”上而无需身份验证的方法。
  2. 继承 ServiceFilterAttribute 来创建一个属性,该属性将您的过滤器应用到您放置该属性的任何控制器。它与身份验证或不身份验证兼容。如果您想绕过任何现有的身份验证/授权系统[AllowAnonymous],只需应用该属性(如果有)。
  3. 使用依赖项注入容器注册在 1. 中创建的过滤器。
  4. 将您在 2. 中创建的属性添加到您想要应用自定义逻辑的任何控制器端点。

结论

这种方法比破解授权功能在没有身份验证的情况下工作要灵活得多,而它显然不是设计来这样做的。另外,您还有一个很大的优势,即无论身份验证/授权系统如何,这都可以工作,因此,如果以后您向 API 添加任何内容,您仍然可以在您想要的任何端点上验证这些参数。IMO 这最终是一种更干净、更简单的方法。


Nev*_*ane 6

当我之前使用策略时,我也将默认的身份验证方案设置到其中。我已经修改了DefaultPolicy所以它略有不同。然而,同样的方法也适用于添加策略。

services.AddAuthorization(options =>
{
    options.AddPolicy(DefaultAuthorizedPolicy, policy =>
    {
        policy.Requirements.Add(new TokenAuthRequirement());
        policy.AuthenticationSchemes = new List<string>()
        {
            CookieAuthenticationDefaults.AuthenticationScheme
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

请考虑默认情况下AuthenticationSchemes属性使用只读列表。我认为最好也实现它而不是 List。

  • 是的。您需要添加 cookie `AddAuthentication().AddCookie()` (3认同)