Spring Security OAuth2 - Simple Token Revocation (met behulp van de Spring Security OAuth legacy-stack)
1. Overzicht
In deze korte tutorial laten we zien hoe we tokens kunnen intrekken die zijn toegekend door een OAuth-autorisatieserver geïmplementeerd met Lente beveiliging.
Wanneer een gebruiker uitlogt, wordt zijn token niet onmiddellijk verwijderd uit de token store; in plaats daarvan blijft het geldig totdat het vanzelf verloopt.
En dus betekent het intrekken van een token dat dat token uit de tokenopslag wordt verwijderd. We behandelen de standaard tokenimplementatie in het framework, niet de JWT-tokens.
Opmerking: dit artikel maakt gebruik van het verouderde Spring OAuth-project.
2. Het TokenStore
Laten we eerst de token store opzetten; we gebruiken een JdbcTokenStore, samen met de bijbehorende gegevensbron:
@Bean public TokenStore tokenStore () {retourneer nieuwe JdbcTokenStore (dataSource ()); } @Bean openbare DataSource dataSource () {DriverManagerDataSource dataSource = nieuwe 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")); retourneer dataSource; }
3. Het DefaultTokenServices Boon
De klasse die alle tokens verwerkt, is de DefaultTokenServices - en moet in onze configuratie als boon worden gedefinieerd:
@Bean @ Primary openbaar DefaultTokenServices tokenServices () {DefaultTokenServices defaultTokenServices = nieuw DefaultTokenServices (); defaultTokenServices.setTokenStore (tokenStore ()); defaultTokenServices.setSupportRefreshToken (true); terugkeer defaultTokenServices; }
4. De lijst met tokens weergeven
Laten we voor admin-doeleinden ook een manier instellen om de momenteel geldige tokens te bekijken.
We krijgen toegang tot het TokenStore in een controller en haal de momenteel opgeslagen tokens op voor een opgegeven client-ID:
@Resource (name = "tokenStore") TokenStore tokenStore; @RequestMapping (method = RequestMethod.GET, value = "/ tokens") @ResponseBody openbare lijst getTokens () {Lijst tokenValues = nieuwe ArrayList (); Verzamelingstokens = tokenStore.findTokensByClientId ("sampleClientId"); if (tokens! = null) {voor (OAuth2AccessToken token: tokens) {tokenValues.add (token.getValue ()); }} retourneer tokenValues; }
5. Een toegangstoken intrekken
Om een token ongeldig te maken, gebruiken we de revokeToken () API van de ConsumerTokenServices koppel:
@Resource (name = "tokenServices") ConsumerTokenServices tokenServices; @RequestMapping (method = RequestMethod.POST, value = "/tokens/revoke/{tokenId:.*}") @ResponseBody openbare String revokeToken (@PathVariable String tokenId) {tokenServices.revokeToken (tokenId); retourneer tokenId; }
Dit is natuurlijk een zeer gevoelige operatie, dus we moeten het ofwel alleen intern gebruiken, of we moeten er goed op letten dat het wordt blootgesteld met de juiste beveiliging.
6. De voorkant
Voor de front-end van ons voorbeeld geven we de lijst met geldige tokens weer, het token dat momenteel wordt gebruikt door de aangemelde gebruiker die het intrekkingsverzoek indient, en een veld waarin de gebruiker het token kan invoeren dat hij wil intrekken:
$ 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 = functie () {$ 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 + "is ingetrokken!"; $ scope.tokenToRevoke = ""; }}
Als een gebruiker opnieuw probeert een ingetrokken token te gebruiken, ontvangt hij een 'ongeldige token'-fout met statuscode 401.
7. Het vernieuwingstoken intrekken
Het vernieuwingstoken kan worden gebruikt om een nieuw toegangstoken te verkrijgen. Telkens wanneer een toegangstoken wordt ingetrokken, wordt het vernieuwingstoken dat ermee is ontvangen, ongeldig gemaakt.
Als we het vernieuwingstoken zelf ook ongeldig willen maken, kunnen we de methode gebruiken removeRefreshToken () van de klas JdbcTokenStore, waarmee het vernieuwingstoken uit de winkel wordt verwijderd:
@RequestMapping (method = RequestMethod.POST, value = "/tokens/revokeRefreshToken/{tokenId:.*}") @ResponseBody public String revokeRefreshToken (@PathVariable String tokenId) {if (tokenStore instanceof JdbcTokenStore) {((JdbcTokenStore) tokenStore tokenStore) {((JdbcTokenStore) tokenStore .removeRefreshToken (tokenId); } return tokenId; }
Om te testen of het vernieuwingstoken niet langer geldig is nadat het is ingetrokken, zullen we de volgende test schrijven, waarin we een toegangstoken verkrijgen, deze vernieuwen, vervolgens het vernieuwingstoken verwijderen en proberen het opnieuw te vernieuwen.
We zullen zien dat we na intrekking de antwoordfout ontvangen: "ongeldig vernieuwingstoken":
openbare klasse TokenRevocationLiveTest {privé String refreshToken; private String gainAccessToken (String clientId, String gebruikersnaam, String wachtwoord) {Map params = new HashMap (); params.put ("grant_type", "wachtwoord"); params.put ("client_id", clientId); params.put ("gebruikersnaam", gebruikersnaam); params.put ("wachtwoord", wachtwoord); Antwoordantwoord = RestAssured.given (). Auth (). preemptive (). basic (clientId, "secret"). en (). met (). params (params). when (). post ("// localhost: 8081 / spring-security-oauth-server / oauth / token"); refreshToken = response.jsonPath (). getString ("refresh_token"); return response.jsonPath (). getString ("access_token"); } private String getRefreshToken (String clientId) {Map params = new HashMap (); params.put ("grant_type", "refresh_token"); params.put ("client_id", clientId); params.put ("refresh_token", refreshToken); Antwoordantwoord = RestAssured.given (). Auth () .preemptive (). Basic (clientId, "secret"). En (). Met (). Params (params) .when (). Post ("// localhost: 8081 / spring-security-oauth-server / oauth / token "); return response.jsonPath (). getString ("access_token"); } private void authorizeClient (String clientId) {Map params = new HashMap (); params.put ("response_type", "code"); params.put ("client_id", clientId); params.put ("scope", "lezen, schrijven"); Antwoordantwoord = RestAssured.given (). Auth (). Preemptive () .basic (clientId, "secret"). En (). Met (). Params (params). when (). post ("// localhost: 8081 / spring-security-oauth-server / oauth / authorize"); } @Test openbare ongeldige gegevenUser_whenRevokeRefreshToken_thenRefreshTokenInvalidError () {String accessToken1 = getAccessToken ("fooClientIdPassword", "john", "123"); String accessToken2 = getAccessToken ("fooClientIdPassword", "tom", "111"); authorizeClient ("fooClientIdPassword"); String accessToken3 = getRefreshToken ("fooClientIdPassword"); authorizeClient ("fooClientIdPassword"); Antwoord refreshTokenResponse = RestAssured.given (). header ("Autorisatie", "Bearer" + accessToken3) .get ("// localhost: 8082 / spring-security-oauth-resource / tokens"); assertEquals (200, refreshTokenResponse.getStatusCode ()); Antwoord revokeRefreshTokenResponse = RestAssured.given () .header ("Autorisatie", "Bearer" + accessToken1) .post ("// localhost: 8082 / spring-security-oauth-resource / tokens / revokeRefreshToken /" + refreshToken); assertEquals (200, revokeRefreshTokenResponse.getStatusCode ()); String accessToken4 = getRefreshToken ("fooClientIdPassword"); authorizeClient ("fooClientIdPassword"); Antwoord refreshTokenResponse2 = RestAssured.given () .header ("Autorisatie", "Bearer" + accessToken4) .get ("// localhost: 8082 / spring-security-oauth-resource / tokens"); assertEquals (401, refreshTokenResponse2.getStatusCode ()); }}
8. Conclusie
In deze zelfstudie hebben we laten zien hoe u een OAuth-toegangstoken en een Oauth-vernieuwingstoken kunt intrekken.
De implementatie van deze tutorial is te vinden in het GitHub-project.