首页 > 解决方案 > 在 Spring Security + Angular 中实现刷新令牌

问题描述

我正在使用 OAuth2 和 JWT开发这个 Spring Security实现:

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

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。当令牌过期时,我是否需要先向端点发送请求以刷新令牌,然后再发送到登录页面?

这个案例应该如何执行?

标签: springspring-bootspring-securityangular9angular-oauth2-oidc

解决方案


在身份验证时,将创建两个 JWT——访问令牌刷新令牌。刷新令牌将具有更长的有效期。两个令牌都将写入cookie,以便在每个后续请求中发送它们。

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


访问令牌携带直接访问资源所需的信息。换句话说,当客户端将访问令牌传递给管理资源的服务器时,该服务器可以使用令牌中包含的信息来决定客户端是否被授权。访问令牌通常有一个到期日期并且是短暂的。

刷新令牌携带获取新访问令牌所需的信息。换句话说,每当需要访问令牌来访问特定资源时,客户端都可以使用刷新令牌来获取由身份验证服务器颁发的新访问令牌。常见用例包括在旧访问令牌过期后获取新访问令牌,或首次访问新资源。刷新令牌也可以过期,但寿命相当长。


高级代码

认证()

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");
        }
    }

刷新令牌()

尝试使用作为 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());
        }
    }

推荐阅读