Введение в аутентификацию и авторизацию
Аутентификация и авторизация в Web API имеет некоторые особенности в отличие от ASP.NET MVC. Здесь фактически нам доступны три вида аутентификации:
стандартная через куки, через внешние сервисы и аутентификация с помощью токена.
В то же время Web API и ASP.NET MVC имеют ряд общих моментов.
Авторизация под разными рабочими аккаунтами
Для получения списка рабочих аккаунтов менеджера и для работы под разными рабочими аккаунтами менеджера необходимо прочитать документацию по рабочим аккаунтам менеджера
Вход в систему
Метод идентифицирует пользователя по переданным email и паролю и если все в порядке, генерирует токен доступа, устанавливает дату действия токена и возвращает его в json.
Выход из системы
В этом методе токен просто отзывается у авторизованного пользователя.
Запрос авторизации под другим пользователем
Возможен следующий сценарий:
- Приложение перенаправляет пользователя на сайт с запросом авторизации.
- Пользователь на сайте уже авторизован и данному приложение доступ уже был
разрешен. - Пользователю будет предложена возможность продолжить работу под текущим аккаунтом,
либо зайти под другим аккаунтом.
Если есть необходимость, чтобы на шаге 3 сразу происходило перенаправление (redirect) с временным токеном,
необходимо добавить к запросу /oauth/authorize… параметр skip_choose_account=true.
В этом случае автоматически выдаётся доступ пользователю авторизованному на сайте.
Инвалидация access-токена
Для того, чтобы инвалидировать текущий access-токен, необходимо сделать запрос:
Передавая его стандартным способом в заголовке в формате:
Authorization: Bearer ACCESS_TOKEN
Инвалидация работает только на действующем access-токене.
После инвалидации токен нельзя будет запросить с помощью refresh-токена – для работы необходимо будет заново авторизоваться в api.
Таким образом можно инвалидировать только токен пользователя.
Исходный код
Полный исходный код рассмотренного приложения находится на
Маршруты для аутентификации
После реализации методов контроллера аутентификации создадим соответствующие маршруты в файле /routes/api.php:
Route::group(['namespace' => 'Api'], function () { Route::post('register', 'AuthController@register'); Route::post('login', 'AuthController@login'); Route::post('logout', 'AuthController@logout')->middleware('auth:api'); });
Два первых маршрута – register и login работают для неавторизованных пользователей. Третий маршрут logout работает через middleware auth – только для авторизованных пользователей. Для запросов по этому маршруту следует передавать в запросе заголовок с токеном доступа:
Authorization: Bearer erJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9…
Теперь все готово. Можно регистрировать нового пользователя через вызов /api/register, передав параметры name, email и password, авторизоваться через вызов /api/login, передав email и password, и выходить из системы через вызов /api/logout.
Настроим spring security
Соберем все проделанное выше вместе и настроим Spring Security.
Немного теории
Аутентификация
Обновление пары access и refresh токенов
access_token также имеет срок жизни (ключ expires_in, в секундах), при его
истечении приложение должно сделать запрос с refresh_token для получения
нового.
Запрос необходимо делать в application/x-www-form-urlencoded.
В теле запроса необходимо передать дополнительные параметры:
Ответ
Ответ будет идентичен ответу на получения токенов в первый раз:
refresh_token можно использовать только один раз и только по истечению
срока действия access_token.
После получения новой пары access и refresh токенов, их необходимо использовать
в дальнейших запросах в api и запросах на продление токена.
Ошибки
400 Bad Request
– ошибка в параметрах запроса.
Переопределим authenticationfailurehandler
В случае, если пользователь не прошел аутентификацию, мы перенаправим его по адресу обратного вызова, который был передан в начале процесса аутентификации с параметром error, содержащим текст ошибки.
public class ExampleAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;
public ExampleAuthenticationFailureHandler(
HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) {
this.authorizationRequestRepository = requireNonNull(authorizationRequestRepository);
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
String targetUrl = getFailureUrl(request, exception);
authorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
redirectStrategy.sendRedirect(request, response, targetUrl);
}
private String getFailureUrl(HttpServletRequest request, AuthenticationException exception) {
String targetUrl = getCookie(request, Cookies.REDIRECT_URI)
.map(Cookie::getValue)
.orElse(("/"));
return UriComponentsBuilder.fromUriString(targetUrl)
.queryParam("error", exception.getLocalizedMessage())
.build().toUriString();
}
}
Получение авторизации пользователя
В начале приложению необходимо направить пользователя (открыть страницу) по адресу:
Обязательные параметры:
response_type=code
— указание на способ получения авторизации, используяauthorization code
client_id
— идентификатор, полученный при создании приложения
Необязательные параметры:
Процесс авторизации
Если пользователь не авторизован на сайте, ему будет показана форма
авторизации на сайте. После прохождения авторизации на сайте, пользователю
будет выведена форма с запросом разрешения доступа вашего приложения к его
персональным данным.
Если пользователь не разрешает доступ приложению, пользователь будет
перенаправлен на указанный redirect_uri с ?error=access_denied и
state={state}, если таковой был указан при первом запросе.
Если пользователь авторизован, ему будет показана форма для подтверждения работы под текущим пользователем или смены пользователя.
Реализация
Мы реализуем REST-сервис, предоставляющий следующее API:
Высокоуровневая архитектура приложения
Заметим, что поскольку приложение состоит из трех взаимодействующих компонентов, помимо того, что мы выполняем авторизацию запросов клиента к серверу, Bitbucket авторизует запросы сервера к нему. Мы не будем настраивать авторизацию методов по ролям, чтобы не делать пример сложнее.
Процесс регистрации OAuth клиента описан в предыдущей статье
Для реализации мы будем использовать Spring Boot версии 2.2.2.RELEASE и Spring Security версии 5.2.1.RELEASE.
Регистрация пользователя
В этом методе создается объект пользователя по переданным параметрам (имя, email, пароль), сохраняется в БД и возвращается сообщение об у спешной регистрации.
Создадим tokenauthenticationfilter
Задача этого фильтра извлечь токен доступа из заголовка Authorization в случае его наличия, провалидировать его и инициализировать секьюрити контекст.
Ссылки
P.S.
Успешное получение временного authorization_code
В случае разрешения прав, в редиректе будет указан
временный authorization_code:
Создадим repositories endpoint
То ради чего и нужна была аутентификация через OAuth2 и Bitbucket — возможность использовать Bitbucket API для доступа к своим ресурсам. Используем Bitbucket repositories API для получения списка репозиториев текущего пользователя.
Создадим login endpoint
Для аутентификации пользователя мы по-прежнему используем OAuth2 с типом авторизации Authorization Code. Однако на предыдущем шаге мы заменили стандартный AuthenticationEntryPoint своей реализацией, поэтому нам нужен явный способ запустить процесс аутентификации.