Схема authenticationScheme не указана, и не найден DefaultChallengeScheme - ASP.NET core 2.1 - PullRequest
0 голосов
/ 17 октября 2018

Я работаю с проектом ASP.NET Core 2.1 WebApi, в котором мы использовали аутентификацию на основе токенов

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

Это работает нормально, если я передаю заголовок для метода авторизации, однако отсутствие заголовка для того же дает ошибку No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

Здесь у меня есть два вопроса:

1) Можно отправить 500 сэто исключение для конечного пользователя, когда заголовок не указан?

2) Как справиться с этим сценарием и передать значимое сообщение «заголовок отсутствует» или что-то?

1 Ответ

0 голосов
/ 18 октября 2018

Можно отправить 500 с этим исключением в конец пользователя, если заголовок не указан?

Боюсь, что это не очень хорошая идея.

Код состояния 500 указывает на ошибку сервера.Когда клиенты отправляют запрос без токена, нет смысла сообщать клиенту, что «произошла внутренняя ошибка».Лучший способ - отправить 401, чтобы бросить вызов пользователю, или отправить 403, чтобы запретить.

Как обработать этот сценарий и передать значимое сообщение «заголовок отсутствует» или что-то в этом роде?

Во-первых, я должен сказать, что я не думаю, что использование AuthorizationFilter для аутентификации пользователя является хорошим вариантом.

Как описывает ошибка, выдается ошибка, поскольку не было указано AuthenticationScheme, а DefaultChallengeScheme не найдено .

Чтобы исправить ошибку, просто укажите схему аутентификации .Например, если вы используете JwtToken, вы должны добавить AddAuthentication(JwtBearerDefaults.AuthenticationScheme) или использовать атрибут [Authorize(AuthenticationSchemes ="JwtBearerDefaults.AuthenticationScheme")]

В противном случае, если вы хотите настроить способ аутентификации пользователя (например, customаутентификация на основе токенов), вы должны создать новый обработчик аутентификации токенов.Уже есть встроенный абстрактный класс 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";
}

, если пользователь отправляет запрос без токена или с неверным токеном, ответ от серверабудет:

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, вы можете использовать следующий код для регистрации аутентификации JwtBearer:

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.

Другой способ переадресовать вызов по:

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
...