Spring Security OAuth2 - Jednostavno opozivanje tokena (pomoću naslijeđenog naslaga Spring Security OAuth)

1. Pregled

U ovom brzom vodiču ilustrirat ćemo kako možemo opozvati tokene dodijeljene od OAuth Autorizacijski poslužitelj provodi s Proljetna sigurnost.

Kad se korisnik odjavi, njegov se token ne uklanja odmah iz spremišta tokena; umjesto toga ostaje na snazi ​​sve dok samostalno ne istekne.

Dakle, opoziv tokena značit će uklanjanje tog tokena iz spremišta tokena. U okviru ćemo pokriti standardnu ​​implementaciju tokena, a ne JWT tokene.

Bilješka: ovaj članak koristi nasljeđeni projekt Spring OAuth.

2. The TokenStore

Prvo, postavimo trgovinu tokena; koristit ćemo a JdbcTokenStore, zajedno s pripadajućim izvorom podataka:

@Bean public TokenStore tokenStore () {return new JdbcTokenStore (dataSource ()); } @Bean public DataSource dataSource () {DriverManagerDataSource dataSource = novi DriverManagerDataSource (); dataSource.setDriverClassName (env.getProperty ("jdbc.driverClassName")); dataSource.setUrl (env.getProperty ("jdbc.url")); dataSource.setUsername (env.getProperty ("jdbc.user")); dataSource.setPassword (env.getProperty ("jdbc.pass")); vratiti dataSource; }

3. The DefaultTokenServices Grah

Klasa koja obrađuje sve tokene je DefaultTokenServices - i mora se definirati kao grah u našoj konfiguraciji:

@Bean @Primary public DefaultTokenServices tokenServices () {DefaultTokenServices defaultTokenServices = novi DefaultTokenServices (); defaultTokenServices.setTokenStore (tokenStore ()); defaultTokenServices.setSupportRefreshToken (true); vrati defaultTokenServices; }

4. Prikaz popisa žetona

U administratorske svrhe, postavimo i način pregleda trenutno važećih tokena.

Pristupit ćemo TokenStore u kontroler i dohvatite trenutno pohranjene tokene za navedeni ID klijenta:

@Resource (name = "tokenStore") TokenStore tokenStore; @RequestMapping (method = RequestMethod.GET, value = "/ tokens") @ResponseBody javni popis getTokens () {Popis tokenValues ​​= novi ArrayList (); Tokeni kolekcije = tokenStore.findTokensByClientId ("sampleClientId"); if (tokeni! = null) {za (OAuth2AccessToken token: tokeni) {tokenValues.add (token.getValue ()); }} return tokenValues; }

5. Opoziv pristupnog tokena

Da bismo poništili token, iskoristit ćemo revokeToken () API iz ConsumerTokenServices sučelje:

@Resource (name = "tokenServices") ConsumerTokenServices tokenServices; @RequestMapping (method = RequestMethod.POST, value = "/tokens/revoke/{tokenId:.*}") @ResponseBody javni niz revokeToken (@PathVariable String tokenId) {tokenServices.revokeToken (tokenId); povratak tokenId; }

Naravno, ovo je vrlo osjetljiva operacija pa bismo je trebali koristiti samo interno ili bismo trebali paziti da je izložimo s odgovarajućom sigurnošću.

6. Prednji kraj

Za prednji kraj našeg primjera prikazat ćemo popis valjanih tokena, token koji trenutno koristi prijavljeni korisnik koji podnosi zahtjev za opoziv i polje u koje korisnik može unijeti token koji želi opozvati:

$ scope.revokeToken = $ resource ("// localhost: 8082 / spring-security-oauth-resource / tokens / revoke /: tokenId", {tokenId: '@ tokenId'}); $ scope.tokens = $ resource ("// localhost: 8082 / spring-security-oauth-resource / tokens"); $ scope.getTokens = function () {$ scope.tokenList = $ scope.tokens.query (); } $ scope.revokeAccessToken = function () {if ($ scope.tokenToRevoke && $ scope.tokenToRevoke.length! = 0) {$ scope.revokeToken.save ({tokenId: $ scope.tokenToRevoke}); $ rootScope.message = "Token:" + $ scope.tokenToRevoke + "je opozvan!"; $ scope.tokenToRevoke = ""; }}

Ako korisnik ponovno pokuša upotrijebiti opozvani token, primit će pogrešku "nevažeći token" sa statusnim kodom 401.

7. Opoziv žetona za osvježavanje

Token za osvježavanje može se koristiti za dobivanje novog pristupnog tokena. Kad god se opozove pristupni token, token osvježavanja koji je s njim primljen je nevažeći.

Ako želimo onesposobiti i sam token za osvježavanje, možemo koristiti metodu removeRefreshToken () razreda JdbcTokenStore, koji će ukloniti token osvježavanja iz trgovine:

@RequestMapping (method = RequestMethod.POST, value = "/tokens/revokeRefreshToken/{tokenId:.*}") @ResponseBody public String revokeRefreshToken (@PathVariable String tokenId) {if (tokenStore instanceof JdbcTokenStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStoreStore .removeRefreshToken (tokenId); } return tokenId; }

Da bismo testirali da token za osvježavanje više nije valjan nakon opoziva, napisat ćemo sljedeći test u kojem ćemo dobiti pristupni token, osvježiti ga, zatim ukloniti token za osvježavanje i pokušati ga ponovno osvježiti.

Vidjet ćemo da ćemo nakon opoziva primiti pogrešku odgovora: “nevažeći token osvježavanja”:

javna klasa TokenRevocationLiveTest {private String refreshToken; private String получитьAccessToken (String clientId, String korisničko ime, String lozinka) {Map params = new HashMap (); params.put ("vrsta_dodatka", "lozinka"); params.put ("client_id", clientId); params.put ("korisničko ime", korisničko ime); params.put ("lozinka", lozinka); Odgovor odgovora = RestAssured.given (). Auth (). preemptive (). basic (clientId, "secret"). i (). with (). params (params). when (). post ("// localhost: 8081 / spring-security-oauth-server / oauth / token"); refreshToken = response.jsonPath (). getString ("refresh_token"); vrati odgovor.jsonPath (). getString ("access_token"); } private String получитьRefreshToken (String clientId) {Map params = new HashMap (); params.put ("grant_type", "refresh_token"); params.put ("client_id", clientId); params.put ("refresh_token", refreshToken); Odgovor odgovora = RestAssured.given (). Auth () .preemptive (). Basic (clientId, "secret"). I (). With (). Params (params) .when (). Post ("// localhost: 8081 / spring-security-oauth-server / oauth / token "); vrati odgovor.jsonPath (). getString ("access_token"); } private void autorizeClient (String clientId) {Map params = new HashMap (); params.put ("vrsta_odgovora", "kôd"); params.put ("client_id", clientId); params.put ("opseg", "čitanje, pisanje"); Odgovor odgovora = RestAssured.given (). Auth (). Preemptive () .basic (clientId, "secret"). I (). With (). Params (params). when (). post ("// localhost: 8081 / spring-security-oauth-server / oauth / odobriti"); } @Test javna praznina givenUser_whenRevokeRefreshToken_thenRefreshTokenInvalidError () {String accessToken1 = получитьAccessToken ("fooClientIdPassword", "john", "123"); Niz accessToken2 = получитьAccessToken ("fooClientIdPassword", "tom", "111"); odobritiClient ("fooClientIdPassword"); Niz accessToken3 = dobitiRefreshToken ("fooClientIdPassword"); odobritiClient ("fooClientIdPassword"); Response refreshTokenResponse = RestAssured.given (). zaglavlje ("Autorizacija", "Donositelj" + accessToken3) .get ("// localhost: 8082 / spring-security-oauth-resource / tokens"); assertEquals (200, refreshTokenResponse.getStatusCode ()); Odgovor revokeRefreshTokenResponse = RestAssured.given () .header ("Autorizacija", "Donositelj" + accessToken1) .post ("// localhost: 8082 / spring-security-oauth-resource / tokens / revokeRefreshToken /" + refreshToken); assertEquals (200, revokeRefreshTokenResponse.getStatusCode ()); Niz accessToken4 = dobitiRefreshToken ("fooClientIdPassword"); odobritiClient ("fooClientIdPassword"); Response refreshTokenResponse2 = RestAssured.given () .header ("Autorizacija", "Nosilac" + accessToken4) .get ("// localhost: 8082 / spring-security-oauth-resource / tokens"); assertEquals (401, refreshTokenResponse2.getStatusCode ()); }}

8. Zaključak

U ovom smo uputstvu pokazali kako opozvati OAuth pristupni token i Oauth token za osvježavanje.

Provedbu ovog vodiča možete pronaći u projektu GitHub.