Автор оригинала: Sampada Wagde.
1. Обзор
В этом кратком руководстве мы покажем, как мы можем добавить функцию выхода из системы в приложение безопасности OAuth Spring .
Мы рассмотрим несколько способов сделать это. Сначала мы рассмотрим, как выйти из нашего пользователя Keycloak из приложения OAuth , как описано в разделе Создание REST API с помощью OAuth2, а затем с помощью прокси-сервера Zuul, который мы видели ранее .
Мы будем использовать стек OAuth в Spring Security 5. Если вы хотите использовать стек Spring Security OAuth legacy, ознакомьтесь с этой предыдущей статьей: Выход из защищенного приложения OAuth (с использованием устаревшего стека) .
2. Выход Из Системы С Помощью Интерфейсного Приложения
Поскольку Токены доступа управляются Сервером авторизации, они должны быть признаны недействительными на этом уровне. Точные шаги для этого будут немного отличаться в зависимости от используемого сервера авторизации.
В нашем примере, в соответствии с Keycloak documentation , для выхода непосредственно из приложения браузера мы можем перенаправить браузер на http://auth-server/auth/realms/{имя области}/протокол/openid-connect/выход из системы?redirect_uri=encodedRedirectUri .
Наряду с отправкой URI перенаправления нам также необходимо передать id_token_hint в Keycloak конечную точку выхода из системы . Это должно содержать закодированное значение id_token .
Давайте вспомним , как мы сохранили access_token , мы также сохраним id_token :
saveToken(token) { var expireDate = new Date().getTime() + (1000 * token.expires_in); Cookie.set("access_token", token.access_token, expireDate); Cookie.set("id_token", token.id_token, expireDate); this._router.navigate(['/']); }
Важно отметить, что для получения маркера ID в полезной нагрузке ответа Сервера авторизации мы должны включить openid в параметр scope .
Теперь давайте посмотрим на процесс выхода из системы в действии.
Мы изменим нашу функцию выход из системы вход Служба приложений :
logout() { let token = Cookie.get('id_token'); Cookie.delete('access_token'); Cookie.delete('id_token'); let logoutURL = "http://localhost:8083/auth/realms/baeldung/protocol/openid-connect/logout? id_token_hint=" + token + "&post_logout_redirect_uri=" + this.redirectUri; window.location.href = logoutURL; }
Помимо перенаправления, нам также необходимо отказаться от токенов доступа и идентификаторов , которые мы получили с Сервера авторизации.
Следовательно, в приведенном выше коде сначала мы удалили токены, а затем перенаправили браузер на Keycloak logout API.
Примечательно, что мы передали URI перенаправления как http://localhost:8089/ – тот, который мы используем во всем приложении, – так что мы окажемся на целевой странице после выхода из системы.
Удаление токенов доступа, идентификатора и Обновления, соответствующих текущему сеансу, выполняется в конце Сервера авторизации. В этом случае наше браузерное приложение вообще не сохранило токен обновления.
3. Выход Из Системы С Помощью Прокси-Сервера Zuul
В предыдущей статье , посвященной обработке маркера обновления, мы настроили наше приложение так, чтобы оно могло обновлять маркер доступа с помощью маркера обновления. Эта реализация использует прокси-сервер Zuul с пользовательскими фильтрами.
Здесь мы увидим, как добавить функцию выхода из системы к вышесказанному.
На этот раз мы будем использовать другой API Keycloak для выхода пользователя из системы. Мы будем вызывать POST на конечной точке выхода из системы для выхода из сеанса с помощью вызова , не связанного с браузером, вместо перенаправления URL-адреса, которое мы использовали в предыдущем разделе.
3.1. Определите маршрут выхода из системы
Для начала давайте добавим еще один маршрут к прокси-серверу в нашем приложении.yml:
zuul: routes: //... auth/refresh/revoke: path: /auth/refresh/revoke/** sensitiveHeaders: url: http://localhost:8083/auth/realms/baeldung/protocol/openid-connect/logout //auth/refresh route
Фактически, мы добавили подмаршрут к уже существующему auth/refresh . Важно, чтобы мы добавили подмаршрут перед основным маршрутом, иначе Zuul всегда будет отображать URL-адрес основного маршрута .
Мы добавили дополнительный маршрут вместо основного, чтобы иметь доступ к HTTP-только refreshToken cookie, который был настроен на очень ограниченный путь as /auth/refresh (и его подпутям). Мы увидим, зачем нам нужен файл cookie в следующем разделе.
3.2. ЗАПИСЬ на Сервер авторизации/выход из системы
Теперь давайте улучшим реализацию CustomPreZuulFilter , чтобы перехватить URL-адрес /auth/refresh/revoke и добавить необходимую информацию для передачи на сервер авторизации.
Параметры формы, необходимые для выхода из системы, аналогичны параметрам запроса Refresh Token , за исключением отсутствия grant_type :
@Component public class CustomPostZuulFilter extends ZuulFilter { //... @Override public Object run() { //... if (requestURI.contains("auth/refresh/revoke")) { String cookieValue = extractCookie(req, "refreshToken"); String formParams = String.format("client_id=%s&client_secret=%s&refresh_token=%s", CLIENT_ID, CLIENT_SECRET, cookieValue); bytes = formParams.getBytes("UTF-8"); } //... } }
Здесь мы просто извлекли токен обновления cookie и отправили необходимые formParams.
3.3. Удалите токен обновления
При отзыве маркера доступа с помощью выход из системы перенаправление как мы видели ранее, связанный с ним Токен обновления также аннулируется Сервером авторизации.
Однако в этом случае файл cookie HttpOnly останется установленным на Клиенте. Учитывая, что мы не можем удалить его с помощью JavaScript, нам нужно удалить его со стороны сервера.
Для этого давайте добавим в реализацию CustomPostZuulFilter , которая перехватывает /auth/refresh/revoke URL-адрес, чтобы он удалил refreshToken cookie при обнаружении этого URL-адреса:
@Component public class CustomPostZuulFilter extends ZuulFilter { //... @Override public Object run() { //... String requestMethod = ctx.getRequest().getMethod(); if (requestURI.contains("auth/refresh/revoke")) { Cookie cookie = new Cookie("refreshToken", ""); cookie.setMaxAge(0); ctx.getResponse().addCookie(cookie); } //... } }
3.4. Удалите маркер доступа из клиента Angular
Помимо отзыва маркера обновления, файл cookie access_token также должен быть удален со стороны клиента.
Давайте добавим метод в наш угловой контроллер, который очищает файл cookie access_token и вызывает сопоставление /auth/refresh/revoke POST:
logout() { let headers = new HttpHeaders({ 'Content-type': 'application/x-www-form-urlencoded; charset=utf-8'}); this._http.post('auth/refresh/revoke', {}, { headers: headers }) .subscribe( data => { Cookie.delete('access_token'); window.location.href = 'http://localhost:8089/'; }, err => alert('Could not logout') ); }
Эта функция будет вызвана при нажатии на кнопку выхода из системы:
4. Заключение
В этом быстром, но подробном уроке мы показали, как мы можем выйти из системы пользователя из OAuth защищенное приложение и аннулирует токены этого пользователя.
Полный исходный код примеров можно найти на GitHub .