Изход в защитено приложение OAuth

1. Общ преглед

В този бърз урок ще покажем как можем да добавим функционалност за излизане към приложение на OAuth Spring Security .

Ще видим няколко начина за това. Първо ще видим как да излезем от нашия потребител на Keycloak от приложението OAuth, както е описано в Създаване на REST API с OAuth2, а след това, като използваме Zuul прокси, който видяхме по-рано.

Ще използваме стека OAuth в Spring Security 5. Ако искате да използвате наследствения стек Spring Security OAuth, разгледайте тази предишна статия: Изход в защитено приложение OAuth (използвайки стария стек).

2. Изход с помощта на приложение отпред

Тъй като маркерите за достъп се управляват от сървъра за разрешаване, те ще трябва да бъдат обезсилени на това ниво. Точните стъпки за това ще бъдат малко по-различни в зависимост от сървъра за оторизация, който използвате.

В нашия пример, съгласно документацията на Keycloak, за излизане директно от приложение на браузъра, можем да пренасочим браузъра към // auth-server / auth / realms / {realm-name} / protocol / openid-connect / logout? 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 в параметъра на обхвата .

Сега нека видим процеса на излизане в действие.

Ще модифицираме излизането от нашата функция в App Service :

logout() { let token = Cookie.get('id_token'); Cookie.delete('access_token'); Cookie.delete('id_token'); let logoutURL = "//localhost:8083/auth/realms/baeldung/protocol/openid-connect/logout? id_token_hint=" + token + "&post_logout_redirect_uri=" + this.redirectUri; window.location.href = logoutURL; }

Освен пренасочването, ние също трябва да отхвърлим токените за достъп и идентификация, които сме получили от сървъра за оторизация.

Следователно в горния код първо изтрихме маркерите и след това пренасочихме браузъра към API за излизане на Keycloak .

За отбелязване е, че пренасочихме URI за пренасочване като // localhost: 8089 / - този, който използваме в цялото приложение - така че ще излезем на целевата страница след излизане.

Изтриването на Access, ID и Refresh Tokens, съответстващи на текущата сесия, се извършва в края на Authorization Server. Нашето приложение за браузър изобщо не беше запазило токена за опресняване.

3. Изход с Zuul прокси

В предишна статия за Работа с Refresh Token, ние сме настроили нашето приложение, за да можем да обновим Access Token, използвайки Refresh Token. Това изпълнение използва Zuul прокси с персонализирани филтри.

Тук ще видим как да добавим функционалността за излизане към горното.

Този път ще използваме друг API на Keycloak за излизане от потребител. Ще извикаме POST на крайната точка за излизане, за да излезем от сесия чрез извикване без браузър , вместо URL пренасочването, което използвахме в предишния раздел.

3.1. Определете маршрута за излизане

Като начало, нека добавим друг маршрут към проксито в нашата application.yml :

zuul: routes: //... auth/refresh/revoke: path: /auth/refresh/revoke/** sensitiveHeaders: url: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/logout //auth/refresh route

Всъщност добавихме под-маршрут към вече съществуващия auth / refresh . Важно е да добавим подмаршрута преди основния маршрут, в противен случай Zuul винаги ще картографира URL адреса на основния маршрут .

Добавихме подмаршрут вместо основен, за да имаме достъп до бисквитката refreshToken само за HTTP , която беше настроена да има много ограничен път като / auth / refresh (и неговите под-пътеки). Ще видим защо ни трябва бисквитката в следващия раздел.

3.2. POST към Authorization Server's / logout

Сега нека подобрим изпълнението на CustomPreZuulFilter, за да прихваща URL адреса / auth / refresh / revoke и да добавим необходимата информация, която да бъде предадена на Authorization Server.

Параметрите на формуляра, необходими за излизане, са подобни на тези на заявката за обновяване на маркера, с изключение на това, че няма 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"); } //... } }

Тук просто извлекохме бисквитката refreshToken и изпратихме в необходимата форма Params.

3.3. Премахнете токена за опресняване

При отмяна на маркера за достъп, използвайки пренасочването за излизане, както видяхме по-рано, свързаният с него токер за опресняване също се обезсилва от сървъра за оторизация.

В този случай обаче бисквитката httpOnly ще остане настроена на клиента. Като се има предвид, че не можем да го премахнем чрез JavaScript, трябва да го премахнем от страна на сървъра.

За това нека добавим към изпълнението на CustomPostZuulFilter, което прихваща URL адреса / auth / refresh / revoke, така че да премахне бисквитката refreshToken, когато срещне този 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. Премахнете маркера за достъп от ъгловия клиент

Освен отмяната на Refresh Token, бисквитката access_token ще трябва да бъде премахната и от страна на клиента.

Нека добавим метод към нашия Angular контролер, който изчиства бисквитката access_token и извиква / auth / refresh / revoke POST mapping:

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 = '//localhost:8089/'; }, err => alert('Could not logout') ); }

Тази функция ще бъде извикана, когато щракнете върху бутона Изход:

Logout

4. Заключение

В този бърз, но задълбочен урок показахме как можем да излезем на потребител от защитено приложение OAuth и да обезсилим маркерите на този потребител.

Пълният изходен код на примерите може да бъде намерен в GitHub.