Logout in an OAuth Secured Application (using the Spring Security OAuth legacy stack) – 在OAuth安全的应用程序中注销(使用Spring Security OAuth遗留栈)

最后修改: 2020年 5月 10日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’re going to show how we can add logout functionality to an OAuth Spring Security application.

在这个快速教程中,我们将展示如何在OAuth Spring Security应用程序中添加注销功能

We will, of course, use the OAuth application described in a previous article – Creating a REST API with OAuth2.

当然,我们将使用之前的文章–用OAuth2创建REST API中描述的OAuth应用。

Note: this article is using the Spring OAuth legacy project. For the version of this article using the new Spring Security 5 stack, have a look at our article Logout in an OAuth Secured Application.

注意:本文使用的是Spring OAuth 传统项目。对于使用新的 Spring Security 5 堆栈的本文版本,请查看我们的文章OAuth 安全应用程序中的注销

2. Remove the Access Token

2.删除访问令牌

Simply put, logging out in an OAuth-secured environment involves rendering the user’s Access Token invalid – so it can no longer be used.

简单地说,在OAuth安全的环境中注销涉及使用户的访问令牌无效 – 所以它不能再被使用。

In a JdbcTokenStore-based implementation, this means removing the token from the TokenStore.

在基于JdbcTokenStore-的实现中,这意味着从TokenStore中删除令牌。

Let’s implement a delete operation for the token. We’re going to use the parimary /oauth/token URL structure here and simply introduce a new DELETE operation for it.

让我们为令牌实现一个删除操作。我们将在这里使用辅助的/oauth/token URL结构,并为其简单地引入一个新的DELETE操作。

Now, because we’re actually using the /oauth/token URI here – we need to handle it carefully. We won’t be able to simply add this to any controller – because the framework already has operations mapped to that URI – with POST and GET.

现在,因为我们实际上是在使用/oauth/token URI–我们需要小心处理它。我们不能简单地将其添加到任何控制器中–因为框架已经将操作映射到该URI中–包括POST和GET。

Instead what we need to do is to define this is a @FrameworkEndpoint – so that it gets picked up and resolved by the FrameworkEndpointHandlerMapping instead of the standard RequestMappingHandlerMapping. That way we won’t run into any partial matches and we won’t have any conflicts:

相反,我们需要做的是将其定义为@FrameworkEndpoint – ,这样它就会被FrameworkEndpointHandlerMapping而不是标准的RequestMappingHandlerMapping接收并解决。这样,我们就不会遇到任何部分匹配,也不会有任何冲突。

@FrameworkEndpoint
public class RevokeTokenEndpoint {

    @Resource(name = "tokenServices")
    ConsumerTokenServices tokenServices;

    @RequestMapping(method = RequestMethod.DELETE, value = "/oauth/token")
    @ResponseBody
    public void revokeToken(HttpServletRequest request) {
        String authorization = request.getHeader("Authorization");
        if (authorization != null && authorization.contains("Bearer")){
            String tokenId = authorization.substring("Bearer".length()+1);
            tokenServices.revokeToken(tokenId);
        }
    }
}

Notice how we’re extracting the token out of the request, simply using the standard Authorization header.

注意我们是如何从请求中提取令牌的,只需使用标准的Authorization头。

3. Remove the Refresh Token

3.删除刷新令牌

In a previous article on Handling the Refresh Token, we have set up our application to be able to refresh the Access Token, using a Refresh Token. This implementation makes use of a Zuul proxy – with a CustomPostZuulFilter to add the refresh_token value received from the Authorization Server to a refreshToken cookie.

在之前关于处理刷新令牌的文章中,我们已经将我们的应用程序设置为能够使用刷新令牌来刷新访问令牌。该实现使用了一个 Zuul 代理 – 带有 CustomPostZuulFilter,以将从授权服务器收到的 refresh_token 值添加到 refreshToken cookie 中。

When revoking the Access Token, as shown in the previous section, the Refresh Token associated with it is also invalidated. However, the httpOnly cookie will remain set on the client, given that we can’t remove it via JavaScript – so we need to remove it from the server side.

如上一节所示,在撤销访问令牌时,与之相关的刷新令牌也会失效。然而,鉴于我们无法通过JavaScript删除它,httpOnly cookie将继续在客户端设置–所以我们需要从服务器端删除它。

Let’s enhance the CustomPostZuulFilter implementation that intercepts the /oauth/token/revoke URL so that it will remove the refreshToken cookie when encountering this URL:

让我们增强拦截/oauth/token/revoke URL的CustomPostZuulFilter实现,以便它在遇到这个URL时删除refreshToken cookie。

@Component
public class CustomPostZuulFilter extends ZuulFilter {
    //...
    @Override
    public Object run() {
        //...
        String requestMethod = ctx.getRequest().getMethod();
        if (requestURI.contains("oauth/token") && requestMethod.equals("DELETE")) {
            Cookie cookie = new Cookie("refreshToken", "");
            cookie.setMaxAge(0);
            cookie.setPath(ctx.getRequest().getContextPath() + "/oauth/token");
            ctx.getResponse().addCookie(cookie);
        }
        //...
    }
}

4. Remove the Access Token from the AngularJS Client

4.从AngularJS客户端移除访问令牌

Besides revoking the access token from the token store, the access_token cookie will also need to be removed from the client side.

除了从令牌存储中撤销访问令牌,还需要从客户端删除access_token cookie。

Let’s add a method to our AngularJS controller that clears the access_token cookie and calls the /oauth/token/revoke DELETE mapping:

让我们为我们的AngularJS控制器添加一个方法,清除access_token cookie并调用/oauth/token/revoke DELETE映射。

$scope.logout = function() {
    logout($scope.loginData);
}
function logout(params) {
    var req = {
        method: 'DELETE',
        url: "oauth/token"
    }
    $http(req).then(
        function(data){
            $cookies.remove("access_token");
            window.location.href="login";
        },function(){
            console.log("error");
        }
    );
}

This function will be called when clicking on the Logout link:

当点击Logout链接时,这个函数将被调用。

<a class="btn btn-info" href="#" ng-click="logout()">Logout</a>

5. Conclusion

5.结论

In this quick but in-depth tutorial, we’ve shown how we can logout a user from an OAuth secured application and invalidate the tokens of that user.

在这个快速但深入的教程中,我们展示了如何从OAuth安全应用程序中注销用户并使该用户的令牌失效。

The full source code of the examples can be found over on GitHub.

示例的完整源代码可以在GitHub上找到over