У меня есть приложение веб-API, размещенное на IIS 10 (Windows Server 2016).Приложение имеет API для входа в систему.API входа в систему украшен пользовательским атрибутом фильтра аутентификации, который реализует IAuthenticationFilter.
[RoutePrefix("api/login")]
[AllowUnAuthorized]
[AuthenticationFilter]
public class LoginController : ApiController
{
[HttpPost]
public IHttpActionResult Login()
{
//Code to return token if passed authentication in the Authentication Filter
}
}
Если учетные данные для входа (имя пользователя и пароль) недействительны, атрибут фильтра проверки подлинности устанавливает ErrorResult в контексте, который возвращает код состояния 401 («неавторизован») с ответным сообщением.
public class AuthenticationFilter : Attribute, IAuthenticationFilter
{
public bool AllowMultiple => false;
public async Task AuthenticateAsync(HttpAuthenticationContext context,
CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization =
request.Headers.Authorization;
if (authorization == null)
{
return;
}
if (authorization.Scheme != "Basic")
{
return;
}
Tuple<string, string> credentials =
ExtractCredentials(request.Headers);
if (credentials == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid
credentials", request);
return;
}
string userName = credentials.Item1;
string password = credentials.Item2;
IAuthenticationService
service=container.Resolve<IAuthenticationService>();
var user = service.GetUser(userName, password);
if (user == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
return;
}
//Code to set principal if authentication passed
}
Это код класса AuthenticationFailureResult.
internal class AuthenticationFailureResult : IHttpActionResult
{
public string ReasonPhrase { get; private set; }
public HttpRequestMessage Request { get; private set; }
public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
{
ReasonPhrase = reasonPhrase;
Request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
private HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = Request;
response.ReasonPhrase = ReasonPhrase;
return response;
}
}
Этот код работал нормально и возвращал код состояния 401 вместе с сообщением, как указано в фразе причины.Однако я не знаю, из-за какого изменения внезапно API возвращает html-страницу, которая обычно возвращается IIS, с сообщением "401 - Несанкционированный: доступ запрещен из-за неверных учетных данных." ответа об ошибке, установленного в фильтре аутентификации.Обратите внимание, что тот же API работает, как и ожидалось, при запуске в IISExpress
Что я сделал до сих пор:
- Проверен Web.config и проверен режим CustomErrorsустановлено значение «Вкл.»
- . Убедитесь, что «Анонимная аутентификация» включена в разделе «Аутентификация» для сайта в IIS
. Добавлено следующее в Web.config
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
Как ни странно, веб-API возвращает правильный ответ JSON, когда аутентификация проходит
Редактировать 1:
Вот метод ChallengeAsync
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
var challenge = new AuthenticationHeaderValue("Basic");
context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
return Task.FromResult(0);
}
Реализация AddChallengeOnUnauthorizedResult
public class AddChallengeOnUnauthorizedResult : IHttpActionResult
{
public AddChallengeOnUnauthorizedResult(AuthenticationHeaderValue challenge, IHttpActionResult innerResult)
{
Challenge = challenge;
InnerResult = innerResult;
}
public AuthenticationHeaderValue Challenge { get; private set; }
public IHttpActionResult InnerResult { get; private set; }
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = await InnerResult.ExecuteAsync(cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Only add one challenge per authentication scheme.
if (!response.Headers.WwwAuthenticate.Any((h) => h.Scheme == Challenge.Scheme))
{
response.Headers.WwwAuthenticate.Add(Challenge);
}
}
return response;
}
}