在 Spring 安全 + Angular 中实施刷新令牌

Implement refresh token in Spring Security + Angular

我正在使用 OAuth2 和 JWT 处理此 Spring 安全性 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。 Token过期后是否需要先向端点发送请求刷新token,然后再向登录页面发送请求?

这个案例应该如何实施?

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

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


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

刷新令牌 包含获取新访问令牌所需的信息。换句话说,每当需要访问令牌来访问特定资源时,客户端都可以使用刷新令牌来获取认证服务器颁发的新访问令牌。常见用例包括在旧访问令牌过期后获取新访问令牌,或首次访问新资源。刷新令牌也可以过期,但是 long-lived.


高级代码

验证()

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