ASP.NET MVC5 Базовая проверка подлинности HTTP и исключение AntiForgeryToken

Я работаю над проектом ASP.NET MVC5 с включенной аутентификацией форм. Проект в настоящее время находится на стадии тестирования и размещен в сети на Azure, но владелец проекта хотел бы отключить весь публичный доступ к сайту (поскольку некоторые части сайта не требуют от пользователя аутентификации вообще).

Для этой фазы тестирования мы решили внедрить базовую HTTP-аутентификацию из этой ссылки. Я изменил код, так что он лучше соответствует моим потребностям:

public class BasicAuthenticationAttribute : FilterAttribute, IAuthorizationFilter
{
    public string BasicRealm { get; set; }

    protected string Username { get; set; }
    protected string Password { get; set; }

    public BasicAuthenticationAttribute(string username, string password)
    {
        this.Username = username;
        this.Password = password;
    }

    public void OnAuthorization (AuthorizationContext filterContext)
    {
        var req = filterContext.HttpContext.Request;
        var auth = req.Headers["Authorization"];
        if (!String.IsNullOrEmpty(auth))
        {
            var cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
            var user = new { Name = cred[0], Pass = cred[1] };

            if (user.Name == Username && user.Pass == Password) 
                return;
        }

        var res = filterContext.HttpContext.Response;
        var alreadySent = HttpContext.Current.Items.Contains("headers-sent");

        if (!alreadySent)
        {
            res = filterContext.HttpContext.Response;
            res.StatusCode = 401;
            res.AppendHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Test"));

        }
    }
}

Я также зарегистрировал его как глобальный фильтр:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorExtendedAttribute());
        filters.Add(new BasicAuthenticationAttribute(AppConfig.BasicUsername, AppConfig.BasicPassword));
    }
}

Однако при запуске проекта возникают некоторые проблемы. Если я использую эту версию кода:

        if (!alreadySent)
        {
            res = filterContext.HttpContext.Response;
            res.StatusCode = 401;
            res.AppendHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Test"));
        }

после успешного входа в систему он постоянно перенаправляется на страницу входа в формы.

Однако, если я добавляю

res.End();

после

res.AppendHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Test"));

Antiforgerytoken в файлах cshtml генерирует System.Web.HttpException

Сервер не может добавлять заголовок после отправки HTTP-заголовков.

Но в этом случае он в конечном итоге успешно аутентифицируется.

В настоящее время я застрял в этом, и понятия не имею, как решить эту проблему, поскольку отключить проверку подлинности не является, и опция, и я не могу удалить все AntiForgeryTokens и их проверку.

Ответ 1

Я бы предложил вам использовать поставщика членства в ASP.NET Identity, поскольку он включен в MVC 5. С его помощью вы можете просто аутентифицировать пользователей, не записывая много кода, как это было в предыдущем членстве. Он также может использовать внешний логин с внутренними файлами cookie, как если бы вы использовали метод FormsAuthentication. Все, что вам нужно, это сделать простые конфигурации в коде, и вам не нужно писать свои настраиваемые фильтры.

Ответ 2

Вы пробовали эту строку до res.AppendHeader(..)?

Response.ClearHeaders();

Ответ 3

Вместо завершения запроса с помощью req.End(), я думаю, вы хотите установить filterContext.Result, как показано ниже. Установка результата на ненулевое значение прервет обработку остальной части конвейера MVC и вызовет отправку ответа в браузер, но он не должен запечатывать ответ как End(), поэтому вы не должны получать исключение из уже отправленных заголовков.

Попробуйте следующее:

filterContext.Result = new HttpUnauthorizedResult();