在Spring Security + Angular中实现刷新令牌

时间:2020-06-30 17:48:19

标签: spring spring-boot spring-security angular9 angular-oauth2-oidc

我正在使用OAuth2和JWT来开发此Spring Security implementation

根据作者的说法,我可以通过以下方式使用令牌访问资源:

To access a resource use (you'll need a different application which has configured ResourceServer):

http localhost:8080/users 'Authorization: Bearer '$ACCESS_TOKEN

关于此步骤:

To use the refresh token functionality:

http --form POST adminapp:password@localhost:9999/oauth/token grant_type=refresh_token refresh_token=$REFRESH_TOKEN

我还不清楚何时需要刷新令牌以及如何将此部分处理到Angular中。 令牌到期后,我需要先向端点发送请求以刷新令牌,然后再发送至登录页面吗?

这种情况应如何实施?

1 个答案:

答案 0 :(得分:3)

在身份验证时,将创建两个JWT-访问令牌刷新令牌。刷新令牌将具有更长的有效期。这两个令牌都将写在 cookies 中,以便在随后的每个请求中发送。

在每个REST API调用中,将从HTTP标头中检索令牌。如果访问令牌是未过期 ,请检查用户的特权并相应地允许访问。如果访问令牌已已过期 ,但刷新令牌已有效 ,请重新创建访问令牌并使用新的有效日期刷新令牌,并通过 Cookies

发送回

访问令牌包含必要的信息以直接访问资源。换句话说,当客户端将访问令牌传递给管理资源的服务器时,该服务器可以使用令牌中包含的信息来确定客户端是否被授权。访问令牌通常具有到期日期且期限短。

刷新令牌包含获取新访问令牌所需的信息。换句话说,无论何时需要访问令牌来访问特定资源,客户端都可以使用刷新令牌来获取认证服务器发布的新访问令牌。常见的用例包括在旧的令牌过期后获取新的访问令牌,或首次访问新资源。刷新令牌也可以过期,但寿命很长。


高级代码

authenticate()

public ResponseEntity<OAuth2AccessToken> authenticate(HttpServletRequest request, HttpServletResponse response, Map<String, String> params) {
        try {
            String username = params.get("username");
            String password = params.get("password");
            boolean rememberMe = Boolean.valueOf(params.get("rememberMe"));
            OAuth2AccessToken accessToken = authorizationClient.sendPasswordGrant(username, password);
            OAuth2Cookies cookies = new OAuth2Cookies();
            cookieHelper.createCookies(request, accessToken, rememberMe, cookies);
            cookies.addCookiesTo(response);
            if (log.isDebugEnabled()) {
                log.debug("successfully authenticated user {}", params.get("username"));
            }
            return ResponseEntity.ok(accessToken);
        } catch (HttpClientErrorException ex) {
            log.error("failed to get OAuth2 tokens from UAA", ex);
            throw new BadCredentialsException("Invalid credentials");
        }
    }

refreshToken()

尝试使用作为cookie提供的刷新令牌刷新访问令牌。请注意,浏览器通常并行发送多个请求,这意味着访问令牌将在多个线程上过期。不过,我们不想向UAA发送多个请求,因此我们需要将结果缓存一定的时间并同步线程,以避免并行发送多个请求。

public HttpServletRequest refreshToken(HttpServletRequest request, HttpServletResponse response, Cookie refreshCookie) {
        //check if non-remember-me session has expired
        if (cookieHelper.isSessionExpired(refreshCookie)) {
            log.info("session has expired due to inactivity");
            logout(request, response); //logout to clear cookies in browser
            return stripTokens(request); //don't include cookies downstream
        }
        OAuth2Cookies cookies = getCachedCookies(refreshCookie.getValue());
        synchronized (cookies) {
            //check if we have a result from another thread already
            if (cookies.getAccessTokenCookie() == null) { //no, we are first!
                //send a refresh_token grant to UAA, getting new tokens
                String refreshCookieValue = OAuth2CookieHelper.getRefreshTokenValue(refreshCookie);
                OAuth2AccessToken accessToken = authorizationClient.sendRefreshGrant(refreshCookieValue);
                boolean rememberMe = OAuth2CookieHelper.isRememberMe(refreshCookie);
                cookieHelper.createCookies(request, accessToken, rememberMe, cookies);
                //add cookies to response to update browser
                cookies.addCookiesTo(response);
            } else {
                log.debug("reusing cached refresh_token grant");
            }
            //replace cookies in original request with new ones
            CookieCollection requestCookies = new CookieCollection(request.getCookies());
            requestCookies.add(cookies.getAccessTokenCookie());
            requestCookies.add(cookies.getRefreshTokenCookie());
            return new CookiesHttpServletRequestWrapper(request, requestCookies.toArray());
        }
    }