代码之家  ›  专栏  ›  技术社区  ›  scorpion5211

InvalidOperationException:未指定authenticationScheme,并且未找到DefaultChallengeScheme

  •  2
  • scorpion5211  · 技术社区  · 8 年前

    我们的启动.cs

    services.AddAuthorization(options =>
                {
                    options.AddPolicy("APIKeyAuth", policyCorrectUser =>
                    {
                        policyCorrectUser.Requirements.Add(new APIKeyAuthReq());
                    });
    
                });
    services.AddSingleton<Microsoft.AspNetCore.Authorization.IAuthorizationHandler, APIKeyAuthHandler>();
    

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

    我们的控制器.cs

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

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

    我们对netcore(来自netframework/Forms身份验证)比较陌生,所以如果有更准确的方法进行这种身份验证,请告诉我。

    错误消息:

    InvalidOperationException:未指定authenticationScheme,并且 没有找到DefaultChallengeScheme。 Microsoft.AspNetCore.Authentication验证AAuthenticationService.ChallengeAsync(HttpContext) 上下文、字符串方案、AuthenticationProperties)

    1 回复  |  直到 8 年前
        1
  •  7
  •   Jeldrik    5 年前

    首选基于令牌的身份验证。但是,如果您确实需要自定义 ApiKeyAuth

    首先,看起来 Authorize("APIKeyAuth") 在这里没有意义,因为我们必须在授权之前对用户进行身份验证。当有传入请求时,服务器不知道用户是谁。所以,让我们移动 ApiKeyAuth公司 Authorization 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
    
    推荐文章