2015-12-26 20 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的同时摆脱障碍和锅炉板代码。也许你想试试看。