@babaevmm

Какова правильная последовательность вызова методов Web Api?

Здравствуйте!
Продолжаю изучение MVC 5 и API. Назрел такой вопрос, на который не могу найти точный ответ. С комбинацией в одном проекте MVC и API мне здесь помогли. Создал проект по шаблону SPA, авторизацию описал кастомную (модель сам описал, база PostgreSql через Dapper). Дошел до проектирования API части и встал вопрос: каков должен быть порядок действий для вызова API через внешние клиенты и если вызов идет через мой же сайт? Иногда подумывал на счет создания прослойки MVC -> WEB API -> DATA, но где то прочитал, что это не самая удачная архитектура - так ли это по вашему?
Возвращаясь к основному вопросу, пока складывается в голове следующее (что уже и пробовал на практике): для вызова какого либо метода в API сначала необходимо вызвать метод для аутентификации (Login), а потом при помощи полученного токена вызвать нужный метод (передав токен в заголовке), описано здесь (metanit.com/sharp/aspnet_webapi/5.2.php). И как я понял так КАЖДЫЙ раз. Это смущает. Нет ли другого метода? Читал про REST, так вот он как бы "не поддерживает состояния", т.е. по идее мы одним запросом все должны получить. Может я ошибаюсь?
Второй вариант: вызывать API через свой сайт (не всегда, а только допустим в окне тестирования или администрирования) - если не авторизовано, то все нормально - он не должен меня допустить. Но теперь для того чтобы хранить токен, я должен внести логику, чтобы во время аутентификации токен сохранился (sessionStorage.setItem(tokenKey, data.access_token);) что опять таки сбивает с толку (нужно позаботиться об этом).
Просьба объяснить и сильно не пинать.
  • Вопрос задан
  • 936 просмотров
Решения вопроса 1
AlekseyNemiro
@AlekseyNemiro
full-stack developer
Маркер доступа (access token) вдается один раз. На стороне клиента сохраняется в sessiongStorage либо в cookies. Используется при каждом запросе к API. Как правило, передавать маркер доступа лучше через заголовки. При использовании HTTPS заголовки будут зашифрованы.

Для повышения безопасности, сервер может выдавать маркер доступа привязанный к конкретному IP (можно еще усложнить, например проверять браузер, тип системы и т.п.). Если адрес клиента не совпадает с адресом в базе, то аннулировать маркер доступа и предлагать клиенту получить новый маркер доступа.

Срок действия маркер доступа может должен быть ограничен. Срок действия зависит от условий использования и необходимой степени безопасности. Например, если маркер привязан к IP, то срок действия вполне может быть продолжительным.

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

class ApiAccess : AuthorizeAttribute
{

  public override void OnAuthorization(HttpActionContext actionContext)
  {
    if (actionContext == null)
    {
      throw new ArgumentNullException("actionContext");
    }

    if (!this.IsAuthorized(actionContext))
    {
      return;
    }
  }

  protected override bool IsAuthorized(HttpActionContext actionContext)
  {
    bool isAuthroized = base.IsAuthorized(actionContext);

    // логика проверки доступа

    IEnumerable<string> authItems;
    if (actionContext.Request.Headers.TryGetValues("Authorization", out authItems))
    {
      var auth = authItems.First().Split(' ');
      var token = service.GetToken(auth.Last());
      // ...
    }

    return isAuthroized;
  }
}

Фильтр добавляется к контроллерам WebAPI, где необходима проверка доступа:

[ApiAccess]
public class FileServerController : ApiController
{

   // ...

}

Со стороны клиента, проще сделать вспомогательный метод, который будет отправлять запросы в API с использованием маркера доступа, а также проверять необходимость получения нового маркера доступа (если сервер вернет ошибку). Примерно, как показано в следующем коде:

let url = '/методAPI';
let data = {}; // параметры запроса
let headers = {
  'Authorization': 'ANYNAMEHERE ' + sessionStorage.getItem('token')
};

$.ajax({
  cache: false,
  processData: false,
  type: 'POST',
  url: url,
  contentType: 'application/json',
  dataType: 'json',
  data: JSON.stringify(data),
  headers: headers,
  success: (result) => {
    // успех
  },
  error: (x, textStatus, errorThrown) => {
     // ошибка

     // на сервер можно сделать исключение для плохих маркеров доступа
     // и проверить, если responseText содержит данный тип исключения,
     // то требовать у пользователя повторную авторизацию
    if (x.responseText) {
       let exception = JSON.parse(x.responseText);
       // AccessDeniedException - тип исключения в WebAPI, 
       // (скорее всего полное имя типа придется указывать)
       if (exception.ExceptionType == 'AccessDeniedException') { 
          // ...
       }
    }
  }
});

Что касается получения пользователем маркера доступа, то это можно сделать любым удобным способом. Например, показывать модальное окно для ввода логина и пароля, или перенаправлять на отдельную страницу.

Если API используется через отдельный (независимый от API) сайт, который авторизует пользователей, то пользователя можно не привлекать к процедуре получения нового маркера доступа, сайт это может сделать сам и передать новый маркер своему пользователю.

Если API используется в браузере, как есть, то маркер доступа можно передавать в параметрах запроса. Однако это небезопасно, т.к. данные будут в открытом виде.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы