当前位置: 首页 > 知识库问答 >
问题:

InvalidOperationException:未指定authenticationScheme,未找到DefaultChallengeScheme

宁良平
2023-03-14

我们有一个NetCore2.1API项目。我们使用请求头来检索API密钥,并根据数据库检查该密钥是否与期望的密钥之一匹配。如果确实如此,那么我们允许请求继续,否则我们希望发回未经授权的响应。

我们的startup.cs

services.AddAuthorization(options =>
            {
                options.AddPolicy("APIKeyAuth", policyCorrectUser =>
                {
                    policyCorrectUser.Requirements.Add(new APIKeyAuthReq());
                });

            });
services.AddSingleton<Microsoft.AspNetCore.Authorization.IAuthorizationHandler, APIKeyAuthHandler>();

我们的APIKeyAuthHandler.cs

public class APIKeyAuthReq : IAuthorizationRequirement { }

    public class APIKeyAuthHandler : AuthorizationHandler<APIKeyAuthReq>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, APIKeyAuthReq requirement)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));
            if (requirement == null)
                throw new ArgumentNullException(nameof(requirement));

            var httpContext = context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext;

            var headers = httpContext.HttpContext.Request.Headers;
            if (headers.TryGetValue("Authorization", out Microsoft.Extensions.Primitives.StringValues value))
            {
                using (DBContext db = new DBContext ())
                {
                    var token = value.First().Split(" ")[1];
                    var login = db.Login.FirstOrDefault(l => l.Apikey == token);
                    if (login == null)
                    {
                        context.Fail();
                        httpContext.HttpContext.Response.StatusCode = 403;
                        return Task.CompletedTask;
                    } else
                    {
                        httpContext.HttpContext.Items.Add("CurrentUser", login);
                        context.Succeed(requirement);
                        return Task.CompletedTask;
                    }
                }
            }
        }
    }

还有我们的controller.cs

    [Route("api/[controller]/[action]")]
    [Authorize("APIKeyAuth")]
    [ApiController]
    public class SomeController : ControllerBase
    {
    }

当有效密钥存在时,一切正常,但当它不存在时,将引发500个内部错误,原因是No authenticationScheme而不是403。

我们对net core(来自net Framework/Forms Authentication)相对较新,因此如果有更准确的方式进行此类身份验证,请让我知道。

错误消息:

无效操作异常:未指定身份验证方案,也未找到默认挑战方案。微软。AspNetCore.认证。身份验证服务。HttpContext上下文,字符串方案,身份验证属性

共有1个答案

吴谦
2023-03-14

基于令牌的身份验证是首选。但是,如果您确实需要一个自定义的ApiKeyAuth方案,那么,这是可能的。

首先,似乎Authorize(“APIKeyAuth”)在这里没有意义,因为我们必须在授权之前对用户进行身份验证。当有传入请求时,服务器不知道用户是谁。因此,让我们将ApiKeyAuthAuthorization移动到Authentication

为此,只需创建一个可用于保存选项的虚拟ApiKeyAuthOpts

public class ApiKeyAuthOpts : AuthenticationSchemeOptions
{
}

和一个简单的ApiKeyAuthHandler来处理身份验证(我只是复制上面的一些代码):

public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOpts>
{
    public ApiKeyAuthHandler(IOptionsMonitor<ApiKeyAuthOpts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) 
        : base(options, logger, encoder, clock)
    {
    }
    
    private const string API_TOKEN_PREFIX = "api-key";

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        string token = null;
        string authorization = Request.Headers["Authorization"];

        if (string.IsNullOrEmpty(authorization)) {
            return AuthenticateResult.NoResult();
        }

        if (authorization.StartsWith(API_TOKEN_PREFIX, StringComparison.OrdinalIgnoreCase)) {
            token = authorization.Substring(API_TOKEN_PREFIX.Length).Trim();
        }

        if (string.IsNullOrEmpty(token)) {
            return AuthenticateResult.NoResult();
        }
        
        // does the token match ?
        bool res =false; 
        using (DBContext db = new DBContext()) {
            var login = db.Login.FirstOrDefault(l => l.Apikey == token);  // query db
            res = login ==null ? false : true ; 
        }

        if (!res) {
            return AuthenticateResult.Fail($"token {API_TOKEN_PREFIX} not match");
        }
        else {
            var id=new ClaimsIdentity( 
                new Claim[] { new Claim("Key", token) },  // not safe , just as an example , should custom claims on your own
                Scheme.Name 
            );
            ClaimsPrincipal principal=new ClaimsPrincipal( id);
            var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name);
            return AuthenticateResult.Success(ticket);
        }
    }
}

最后,我们还需要一些配置来让它们工作:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddAuthentication("ApiKeyAuth")
            .AddScheme<ApiKeyAuthOpts,ApiKeyAuthHandler>("ApiKeyAuth","ApiKeyAuth",opts=>{ });
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...
    app.UseAuthentication();
    app.UseHttpsRedirection();
    app.UseMvc();
}

当您向受[Authorize]保护的操作方法发送请求时:

GET https://localhost:44366/api/values/1 HTTP/1.1
Authorization: api-key xxx_yyy_zzz

响应将是HTTP/1.1 200 OK。当您发送没有正确密钥的请求时,响应将是:

HTTP/1.1 401 Unauthorized
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcOVw5LTEyXFNPLkFwaUtleUF1dGhcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET
Date: Wed, 12 Sep 2018 08:33:23 GMT
Content-Length: 0
 类似资料:
  • 我正在使用Deploy azure应用程序服务在Team Services中插入构建步骤,我希望能够将PackageAsSingleFile设置为False来构建我的解决方案。但是,当我尝试使用创建的工件的路径发布包时,我得到“未找到具有指定模式的包”。 我是否需要创建两个项目,一个作为zip文件,一个作为文件包才能做我想做的事情?我已经尝试了不同的文件夹路径,但无论如何我都会得到相同的错误。

  • 我已尝试将映像存储库推送到 IIS 服务器。我已将图像文件夹设置为在发布工件中发布的路径。生成管道成功运行,没有任何问题。但在发布管道中,收到错误“2022-01-03T05:02:47.5647566Z ##[error]错误:未找到具有指定模式的包。 检查任务中提到的包是在生成还是前一阶段中作为项目发布,并在当前作业中下载。在这里,我附加了构建工件映像和发布管道映像。请让我知道这个问题的解决方

  • 我在连接PHP和在远程计算机上运行的SQL SERVER 2008时遇到了困难。我有php版本5.4。3和Apache2.2。22.我的计算机上已经运行了SQL SERVER 2008。我的wamp\bin\php\php5中包含以下文件。4.3\ext文件夹: php_sqlsrv_54_ts.dll php_pdo_sqlsrv_54_ts.dll 运行简单的代码,比如 我遇到以下错误: 但是

  • 问题内容: 这是我的代码: 我收到错误消息: 当我删除第一行时,我得到: 我到处搜索,人们到处都提到了Node.js的问题,但是我安装的Node是正确的,所以我不确定是什么问题。 问题答案: XMLHttpRequest是 Web浏览器中 的内置对象。 它不随Node一起分发;您必须单独安装它, 用npm安装 现在,您可以在代码中使用它。 var xhr = new XMLHttpRequest(

  • 我对和eclipse很熟悉。我从eclipse市场上安装了eclipse,然后安装了javafx。我用场景生成器生成了一个fxml代码,但我无法执行它。我真的被阻止了,找不到任何解决方案 我在运行配置中添加了 -- 作为参数,但没有机会

  • 当我运行我的Android应用程序从eclipse,我得到这个错误。 从日蚀错误复制粘贴 然而,我的亚洲开发银行就在它说的不在的地方。 出了什么问题,如何解决? 我将cd放入adb所在的目录()中,输入adb并显示 adb是绿色的,这意味着它是可执行的,对吗? 例如,dx也是绿色的,当我在命令提示符中输入dx时,它工作了... adb怎么了?