我正在使用 ASP.NET Core 2.1 WebApi 项目,因为我们使用了基于 token 的身份验证

public class UserIdentityFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        StringValues authorizationHeaders;
        if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationHeaders))
            return;
        ...
        ...
    }
}

并具有用于错误处理的中间件:
public async Task Invoke(HttpContext context, ILogger logger, IAppConfiguration appConfiguration)
{
    try
    {
        await _next(context);
    }
    catch (Exception ex)
    {
        await HandleExceptionAsync(context, ex, logger, appConfiguration);
    }
}

如果我为授权方法传递 header ,它工作正常,但是缺少 header 会导致错误 No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
这里我有两个问题:

1) 没有指定 header 时,可以向用户端发送 500 与此异常吗?

2)如何处理这种情况并传递有意义的消息“标题丢失”之类的?

最佳答案



恐怕这不是个好主意。
500 状态代码表示存在服务器错误。当客户端发送没有 token 的请求时,告诉客户端“发生内部错误”是没有意义的。更好的方法是发送 401 来挑战用户或发送 403 来 forbid 。



首先,我不得不说我不认为使用 AuthorizationFilter 来验证用户是一个不错的选择。

正如错误所描述的, 错误被抛出,因为没有指定 AuthenticationScheme ,并且没有找到 DefaultChallengeScheme

要修复该错误, 只需指定一个身份验证方案 。例如,如果您使用 JwtToken ,则应添加 AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 或使用 [Authorize(AuthenticationSchemes ="JwtBearerDefaults.AuthenticationScheme")] 属性

否则,如果您想自定义它对用户进行身份验证的方式(例如,自定义基于 token 的身份验证),您应该创建一个新的 token 身份验证处理程序。已经有一个内置的抽象 AuthenticationHandler 类:

public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler
    where TOptions : AuthenticationSchemeOptions, new()
{
     // ...
}

由于默认 HandleChallengeAsync() 将发送 401 响应,您可以简单地扩展 AuthenticationHandler 并覆盖 HandleChallengeAsync() 方法来自定义您自己的消息来挑战用户 :
public class OurOwnAuthenticationHandler : AuthenticationHandler<ApiKeyAuthOpts>
{
    public OurOwnAuthenticationHandler(IOptionsMonitor<ApiKeyAuthOpts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }


    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        StringValues authorizationHeaders;
        if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationHeaders))
             return AuthenticateResult.NoResult();
        // ... return AuthenticateResult.Fail(exceptionMessage);
        // ... return AuthenticateResult.Success(ticket)
    }

    protected override Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 401;
        var message = "tell me your token";
        Response.Body.Write(Encoding.UTF8.GetBytes(message));
        return Task.CompletedTask;
    }

    protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 403;
        var message = "you have no rights";
        Response.Body.Write(Encoding.UTF8.GetBytes(message));
        return Task.CompletedTask;
    }

}

最后,您还需要注册身份验证处理程序:
services.AddAuthentication("OurOwnAuthN")
        .AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own AuthN Scheme",opts=>{
            // ...
        });

如果您不想将“OurOwnAuthN”设置为默认身份验证方案,您可以使用 [Authorize(AuthenticationSchemes ="OurOwnAuthN")] 来保护您的资源:
// your `ConfigureServices()`
services.AddAuthentication()
        .AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own AuthN Scheme",opts=>{
            // ...
        });


// your action method :
// GET api/values/5
[Authorize(AuthenticationSchemes ="OurOwnAuthN")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
    return "value";
}

如果用户发送没有 token 或 token 不正确的请求,来自服务器的响应将是:
HTTP/1.1 401 Unauthorized
Transfer-Encoding: chunked
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET

tell me your token

[编辑]

如果您使用 Jwt Token ,您可以使用以下代码注册 JwtBearer Authentication :
services.AddAuthentication(options => {
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = Configuration["Jwt:Issuer"],
        ValidAudience = Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
    };
});

[Edit2]

JwtBearer AuthenticationHandler 提供了一个 Challenge 来自定义 WWW-Authenticate :
    .AddJwtBearer(options => {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
        options.Challenge ="tell me your token";;
    })

响应将是:
HTTP/1.1 401 Unauthorized
Server: Kestrel
WWW-Authenticate: tell me your token, error="invalid_token"
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET
Content-Length: 0

请注意 WwW-Authenticate header 。

另一种方法是通过以下方式将 挑战转发给 :
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
        options.ForwardChallenge = "OurOwnAuthN";
    })
    .AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own Authentication Scheme",opts=>{
            // ...
     });

响应将是:
HTTP/1.1 401 Unauthorized
Transfer-Encoding: chunked
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET

tell me your token

关于c# - 未指定 authenticationScheme,也未找到 DefaultChallengeScheme - ASP.NET core 2.1,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52854049/

10-10 15:33