我使用这段代码https://github.com/gdongus/spring-boot-oauth-jwt-example,一切都很完美,但我不知道如何实现注销功能。有人可以给我建议吗?谢谢。
我使用这段代码https://github.com/gdongus/spring-boot-oauth-jwt-example,一切都很完美,但我不知道如何实现注销功能。有人可以给我建议吗?谢谢。
因此,您必须在称为令牌存储的容器中记住每个已发布的令牌。
有一些实现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);
}
这差不多就是全部内容了。现在你可以按照自己的需求实现注销功能,也许可以使用一个特殊的端点,在那里你只需要获取令牌并使用以下代码从令牌存储中删除它:
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时可以摆脱障碍和样板代码。也许你想试一试。
由于注销完成后,认证服务器上的访问令牌和刷新令牌都将从底层存储中删除,因此我们只需要关心资源服务器中的访问令牌失效时间,直到其过期即可。
为了实现这一点,您需要通过Spring Stream/Integration从auth-server
发布一个事件,表明注销已完成,并使所有令牌受众实例订阅Logout事件。
您可以添加自己的LogoutHandler
,从认证服务器发布此事件。Spring cloud stream中的@StreamListner
可用于在每个资源服务器上监听此类事件。
此注销事件必须包含已删除的访问令牌及其过期前剩余的时间。此事件的所有接收者必须将这些访问令牌存储到内存中的黑名单中,并确保如果接收到的访问令牌与任何现有的黑名单令牌匹配,则拒绝对资源的访问。访问令牌过期后,只需从内存中删除它。为了自动过期密钥,您可以使用guava中的CacheBuilder
之类的工具。
总体而言,据我所知,由于JWT的本质,没有现成的解决方案来处理访问令牌的过期问题。