我正在针对 Quickbooks 构建一个 Java Spring Boot OAuth2 客户端应用程序,我正在使用 Spring Security 对 OAuth2 客户端开发的本机支持(spring-documentation)并且能够成功获取访问令牌和刷新令牌,谢谢注释@RegisteredOAuth2AuthorizedClient.
问题是 Quickbooks 在授权代码交换访问令牌的 OAuth2 步骤中发送连接到预期 OAuth2 客户端应用程序的帐户(和帐户中的特定公司)的帐户 ID(他们称之为 realmId)。在开发人员工具上看到网络调用时,我可以看到 302 到 http://localhost/8080/?code=<>&state=<>&realmId=<>(如图所示),我的应用程序中需要这个 realmId我找不到抓住它的方法。
OAuth2AuthorizedClient 对象为我提供了访问令牌和刷新令牌,但无法到达之前的 OAuth2 授权代码交换舞步以获取访问令牌,我需要处理该访问令牌才能获取 realmId。
根据 Quickbooks,没有可以访问的 API(有或没有持有者令牌)来获取 realmId,唯一的方法是在 OAuth2 舞蹈之间进行干预并获取它。
这是一个示例
OAuth2AuthorizationRequestResolver
在授权代码流期间在会话中保存realmId
:
@Component
static class MyOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
private final OAuth2AuthorizationRequestResolver defaultResolver;
public MyOAuth2AuthorizationRequestResolver(InMemoryClientRegistrationRepository clientRegistrationRepository) {
defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(
clientRegistrationRepository,
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
saveRealmIdInSession(request);
return defaultResolver.resolve(request);
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
saveRealmIdInSession(request);
return defaultResolver.resolve(request, clientRegistrationId);
}
private void saveRealmIdInSession(HttpServletRequest request) {
final var realmIds = request.getParameterValues("realmId");
if (realmIds.length < 1) {
return;
}
SessionSupport.setAttribute("realmId", realmIds[0]);
}
}
static class SessionSupport {
static Optional<HttpSession> getSession() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes servletAttributes) {
return Optional.ofNullable(servletAttributes.getRequest()).flatMap(req -> Optional.ofNullable(req.getSession()));
}
return Optional.empty();
}
static Object getAttribute(String name) {
return getSession().map(session -> session.getAttribute(name)).orElse(null);
}
static void setAttribute(String name, Object value) {
getSession().ifPresent(session -> session.setAttribute(name, value));
}
}
您将其添加到您的安全过滤器链配置中,如下所示:
@Bean
SecurityFilterChain clientSecurityFilterChain(
HttpSecurity http,
OAuth2AuthorizationRequestResolver authorizationRequestResolver)
throws Exception {
http.oauth2Login(oauth2 -> oauth2.authorizationEndpoint(authorization -> authorization.authorizationRequestResolver(authorizationRequestResolver)));
...
return http.build();
}
之后您要做的就是阅读会话以获取
realmId
,访问它的方式与保存在MyOAuth2AuthorizationRequestResolver
中时相同。