2015-12-26 120 views

回答

7

客戶端註銷很簡單,只需放棄您擁有的令牌。爲了提供服務器端註銷功能,您的應用程序必須知道當前已通過身份驗證的客戶端,換句話說,現有令牌。基於令牌的身份驗證的「內置」問題是,如果令牌被髮布,它將有效直至失效並且沒有「遠程失效」解決方案。您唯一的機會是避免使用您不再信任的令牌訪問請求。

所以你必須記住一個名爲token store的容器中的每個已發佈的令牌。

TokenStore接口的一些實現可以在內存中工作,也可以在數據庫中工作(JdbcTokenStore)。舉個簡單的例子,InMemoryTokenStore就足夠了。

要使用它,必須按如下所示創建和配置令牌存儲。

添加到您的AuthorizationServerConfiguration

@Bean 
public InMemoryTokenStore tokenStore() { 
    return new InMemoryTokenStore(); 
} 

而在AuthorizationServerEndpointsConfigurer使用它:

@Override 
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception { 
    configurer.authenticationManager(authenticationManager); 
    configurer.userDetailsService(userDetailsService); 
    configurer.accessTokenConverter(accessTokenConverter()); 
    configurer.tokenStore(tokenStore()); 
} 

也將它添加到您的ResourceServerConfiguration

@Autowired 
private InMemoryTokenStore inMemoryTokenStore; 
... 
@Override 
public void configure(ResourceServerSecurityConfigurer resources) throws Exception { 
    resources.resourceId("resource").tokenStore(inMemoryTokenStore); 
} 

這是幾乎所有的。現在像你需要它,也許有一個特殊的端點,在你只有得到令牌(S),你可以實現你的註銷功能,並從令牌存儲與刪除:

inMemoryTokenStore.removeAccessToken(accessToken); 
inMemoryTokenStore.removeRefreshToken(refreshToken); 

注意還去除刷新令牌,否則(如果只有訪問令牌被刪除),客戶端可以通過刷新令牌獲得新的令牌。

這是根據你的測試,以驗證它的工作測試用例:

@Test 
public void getUserWithValidAuth() throws Exception { 
    final HttpHeaders headers = getHttpHeader(CLIENT_USER, CLIENT_SECRET); 
    final HttpEntity<String> request = new HttpEntity<>(headers); 

    final String tokenUrl = getOAuthTokenUrl(OAUTH_TOKEN_USERNAME, OAUTH_TOKEN_PASSWORD); 
    final ResponseEntity<Object> response = restTemplate.exchange(tokenUrl, HttpMethod.POST, request, Object.class); 
    assertTrue("Did not get auth tokens!", response.getStatusCode().is2xxSuccessful()); 

    final Map result = (Map) response.getBody(); 
    final String accessTokenAsString = (String) result.get(ACCESS_TOKEN); 
    final String refreshTokenAsString = (String) result.get(REFRESH_TOKEN); 

    final String resourceUrlWithToken = "http://localhost:" + port + "/users?access_token=" + accessTokenAsString; 

    final ResponseEntity<String> userResponse = restTemplate.exchange(resourceUrlWithToken, HttpMethod.GET, null, 
      String.class); 
    assertTrue("Could not request user data!", userResponse.getStatusCode().is2xxSuccessful()); 

    final OAuth2AccessToken accessToken = inMemoryTokenStore.readAccessToken(accessTokenAsString); 
    final OAuth2RefreshToken refreshToken = inMemoryTokenStore.readRefreshToken(refreshTokenAsString); 
    inMemoryTokenStore.removeAccessToken(accessToken); 
    inMemoryTokenStore.removeRefreshToken(refreshToken); 

    try { 
     restTemplate.exchange(resourceUrlWithToken, HttpMethod.GET, null, String.class); 
     fail("Should not get here, expected 401 for request with access token!"); 
    } catch (HttpClientErrorException e) { 
     // would not be needed with MockMvc 
    } 

    final String refreshTokenUrl = REFRESH_TOKEN_URL + refreshTokenAsString; 
    try { 
     restTemplate.exchange(refreshTokenUrl, HttpMethod.POST, request, Object.class); 
     fail("Should not get here, expected 401 for request with refresh token!"); 
    } catch (HttpClientErrorException e) { 
     // would not be needed with MockMvc 
    } 
} 

而且至少只是一個建議,使用MockMvc是一個真棒測試框架,可以很容易地測試REST調用和你可以在使用RestTemplate的同時擺脫障礙和鍋爐板代碼。也許你想試試看。