Spring 5,401 返回后带有 Google 刷新访问令牌的 OAuth2
Posted
技术标签:
【中文标题】Spring 5,401 返回后带有 Google 刷新访问令牌的 OAuth2【英文标题】:Spring 5, OAuth2 with Google refresh access token after 401 return 【发布时间】:2021-07-05 07:14:49 【问题描述】:我有简单的配置应用程序
spring.security.oauth2.client.registration.google.clientId=xyz
spring.security.oauth2.client.registration.google.clientSecret=xyz
spring.security.oauth2.client.registration.google.scope=email,profile,openid,https://www.googleapis.com/auth/calendar,https://www.googleapis.com/auth/spreadsheets
我使用 http://localhost:8080/oauth2/authorization/google 登录并正常保存到谷歌日历。
获取访问令牌如下所示:
private String getAccessToken()
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String accessToken = null;
if (authentication.getClass().isAssignableFrom(OAuth2AuthenticationToken.class))
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
String clientRegistrationId = oauthToken.getAuthorizedClientRegistrationId();
OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient(
clientRegistrationId, oauthToken.getName());
accessToken = client.getAccessToken().getTokenValue();
return accessToken;
OAuth2AuthorizedClient
包含刷新令牌。
但是 1 小时后访问令牌过期,我不知道如何刷新它。无需重新登录。
https://developers.google.com/calendar/v3/errors#401_invalid_credentials 说
使用长期刷新令牌获取新的访问令牌。
我找到了这个Spring Google OAuth2 With Refresh Token,但对我不起作用。
在 401 之后也有重新登录消息,但它不是用户友好的。
你能帮帮我吗?提前致谢。
【问题讨论】:
我对 Java 帮不上什么忙,但是如果您查看此示例 developers.google.com/analytics/devguides/reporting/core/v4/…,请查看它如何使用 FileDataStoreFactory 如果您请求离线访问,您将获得一个刷新令牌,GoogleAuthorizationCodeFlow 可以使用它来加载需要时提供新的访问权限 您是否尝试过遵循日历 API quickstart? Iamblichus,是的,我做到了,我解决了我的问题,如下所示。要使用 OAuth2 Google 进行身份验证,我使用 Spring 内置机制。它是接收访问令牌和刷新令牌,然后我使用它们通过 Google lib 连接到 Google 日历。 【参考方案1】:感谢 DaImTo 的提示。
我更改了我的凭据用法
Credential credential = new GoogleCredential().setAccessToken(tokenUtils.getAccessToken());
到
private Calendar getClient() throws GeneralSecurityException, IOException
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
Credential credential = new GoogleCredential.Builder()
.setJsonFactory(JSON_FACTORY)
.setTransport(httpTransport)
.setClientSecrets(clientId, clientSecret)
.build()
.setAccessToken(tokenUtils.getAccessToken())
.setRefreshToken(tokenUtils.getRefreshToken());
return new Calendar.Builder(httpTransport, JSON_FACTORY, credential)
.setApplicationName("appname").build();
它有效!
但只有在设置了prompt
的值select_account
或consent
时,应用才会收到刷新令牌。
带值none
或不带prompt
刷新令牌始终为空(access_type=offline
)。
这也不友好,因为应用程序用户每次都必须选择谷歌帐户......
你知道一些解决方法吗?
编辑:
public class TokenUtils
private final OAuth2AuthorizedClientService authorizedClientService;
public TokenUtils(OAuth2AuthorizedClientService authorizedClientService)
this.authorizedClientService = authorizedClientService;
public String getAccessToken()
OAuth2AuthorizedClient client = getClient();
return client.getAccessToken().getTokenValue();
public String getRefreshToken()
OAuth2AuthorizedClient client = getClient();
return client.getRefreshToken().getTokenValue();
public OAuth2AuthorizedClient getClient()
OAuth2AuthenticationToken oauthToken = getOAuthToken();
return authorizedClientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName());
private OAuth2AuthenticationToken getOAuthToken()
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication.getClass().isAssignableFrom(OAuth2AuthenticationToken.class))
return (OAuth2AuthenticationToken) authentication;
throw new SecurityException("Authentication is not OAuth2AuthenticationToken");
编辑 2:
http
.oauth2Login()
.successHandler(new RedirectToPrevAuthSuccessHandler())
.userInfoEndpoint()
.oidcUserService(addRolesOidcUserService);
.oidcUserService(addRolesOidcUserService)
.and()
.authorizationEndpoint()
.authorizationRequestResolver(new AddParamsRequestResolver(
this.clientRegistrationRepository))
;
和
public class AddParamsRequestResolver implements OAuth2AuthorizationRequestResolver
private final OAuth2AuthorizationRequestResolver defaultAuthorizationRequestResolver;
public AddParamsRequestResolver(ClientRegistrationRepository clientRegistrationRepository)
this.defaultAuthorizationRequestResolver =
new DefaultOAuth2AuthorizationRequestResolver(
clientRegistrationRepository,
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request)
OAuth2AuthorizationRequest authorizationRequest =
this.defaultAuthorizationRequestResolver.resolve(request);
if (authorizationRequest != null)
return addParams(authorizationRequest);
return null;
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId)
OAuth2AuthorizationRequest authorizationRequest =
this.defaultAuthorizationRequestResolver.resolve(
request, clientRegistrationId);
if (authorizationRequest != null)
return addParams(authorizationRequest);
return null;
private OAuth2AuthorizationRequest addParams(
OAuth2AuthorizationRequest authorizationRequest)
Map<String, Object> additionalParameters =
new LinkedHashMap<>(authorizationRequest.getAdditionalParameters());
additionalParameters.put("access_type", "offline");
additionalParameters.put("prompt", "consent");
// additionalParameters.put("prompt", "select_account");
// additionalParameters.put("prompt", "login");
return OAuth2AuthorizationRequest.from(authorizationRequest)
.additionalParameters(additionalParameters)
.build();
【讨论】:
可能是因为 localhost...***.com/a/14929345/13729723 什么是 tokenUtils? @JoaquínL.Robles 我在帖子中添加了 tokenUtils。起来。 @Dalmto 我们如何在最新版本的谷歌库中做同样的事情 - GoogleCredential 已被弃用。 感谢@wojek,我添加了客户端引用,但 client.getRefreshToken() 始终为空,缺少什么?以上是关于Spring 5,401 返回后带有 Google 刷新访问令牌的 OAuth2的主要内容,如果未能解决你的问题,请参考以下文章
带有 ROLE_ANONYMOUS 的 AngularJS 和 Spring Security 仍然返回 401
CORS 预检请求返回带有 Windows 身份验证的 HTTP 401
带有帖子的 Spring WebMvcTest 返回 403
Spring Security 返回 200 而不是 HttpWebHandlerAdapter 所述的 401