Spring-boot JWT 登出

17
3个回答

14
客户端登出很简单,只需丢弃您拥有的令牌即可。要提供服务器端登出功能,您的应用程序必须知道当前已认证的客户端,换句话说,就是现有的令牌。基于令牌的身份验证的“内置”问题是,如果令牌被发布,则令牌在到期之前有效,并且没有“远程失效”解决方案。您唯一的机会是避免使用您不再信任的令牌的请求访问。

因此,您必须在称为令牌存储的容器中记住每个已发布的令牌。

有一些实现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时可以摆脱障碍和样板代码。也许你想试一试。


这个答案与JWT无关。JWT是自包含的令牌。如果令牌存储在授权服务器和资源服务器之间共享,则JWT的整个意义就被打败了。 - Ashok Koyi
没错。所以阿肖克是对的,这可能不是一个“生产就绪”的示例,只是一个提示。 - Kevin Peters

1

由于注销完成后,认证服务器上的访问令牌和刷新令牌都将从底层存储中删除,因此我们只需要关心资源服务器中的访问令牌失效时间,直到其过期即可。

为了实现这一点,您需要通过Spring Stream/Integration从auth-server发布一个事件,表明注销已完成,并使所有令牌受众实例订阅Logout事件。

您可以添加自己的LogoutHandler,从认证服务器发布此事件。Spring cloud stream中的@StreamListner可用于在每个资源服务器上监听此类事件。

此注销事件必须包含已删除的访问令牌及其过期前剩余的时间。此事件的所有接收者必须将这些访问令牌存储到内存中的黑名单中,并确保如果接收到的访问令牌与任何现有的黑名单令牌匹配,则拒绝对资源的访问。访问令牌过期后,只需从内存中删除它。为了自动过期密钥,您可以使用guava中的CacheBuilder之类的工具。

总体而言,据我所知,由于JWT的本质,没有现成的解决方案来处理访问令牌的过期问题。


0
您可以创建一个USER_TOKEN表,并保存用户生成的所有令牌,[有助于多设备登录],当从前端调用注销时,从前端本地存储中删除或销毁令牌,并使用相同的令牌调用/logout API,然后验证令牌并输入服务器代码并从USER_TOKEN表中删除该特定令牌。
因此,下次如果有人想使用相同的令牌访问API,则该令牌将无法验证,因为它不存在于数据库中。
但是,如果有人关闭浏览器选项卡而不是注销,则该令牌将在到期之前起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接