Spring security Spring Oauth2客户端,自动刷新过期的访问令牌
让我解释一下我的用例 我需要一个SpringBootOAuth2客户机应用程序(不是资源服务器,因为我们已经有了一个单独的资源服务器)。我还有以下要求:Spring security Spring Oauth2客户端,自动刷新过期的访问令牌,spring-security,oauth-2.0,spring-security-oauth2,spring-oauth2,Spring Security,Oauth 2.0,Spring Security Oauth2,Spring Oauth2,让我解释一下我的用例 我需要一个SpringBootOAuth2客户机应用程序(不是资源服务器,因为我们已经有了一个单独的资源服务器)。我还有以下要求: 对于每个发送到资源服务器的请求,我们需要发送id_令牌。(通过自定义resttemplate完成) 对于任何请求,无论是否调用资源服务器,如果访问令牌过期,我的应用程序必须自动刷新它(无需任何用户干预,如弹出或重定向)。 如果刷新令牌也已过期,则用户必须注销 问题: 对于第2点和第3点,我花了很多时间阅读文档、代码和堆栈溢出,但未能找到解决方案
/**
*
*@作者阿甘
*
*/
@组成部分
公共类ExpiredTokenFilter扩展OncePerRequestFilter{
私有静态最终记录器log=LoggerFactory.getLogger(ExpiredTokenFilter.class);
私有持续时间accessTokenExpiresSkew=持续时间(百万分之1000);
专用时钟=Clock.systemUTC();
@自动连线
专用OAuth2AuthorizedClient服务OAuth2AuthorizedClient服务;
@自动连线
CustomOidcUserService用户服务;
私有DefaultRefreshTokenResponseClient访问TokenResponseClient;
私人JwtDecoderFactory JwtDecoderFactory;
私有静态最终字符串无效\u ID\u令牌\u错误\u CODE=“无效\u ID\u令牌”;
公共过期TokenFilter(){
超级();
this.accessTokenResponseClient=新的DefaultRefreshTokenResponseClient();
this.jwtDecoderFactory=新的OidcIdTokenDecoderFactory();
}
@凌驾
受保护的void doFilterInternal(HttpServletRequest请求、HttpServletResponse响应、FilterChain FilterChain)
抛出ServletException、IOException{
调试(“我的自定义过滤器被调用”);
/**
*检查身份验证是否完成。
*/
if(null!=SecurityContextHolder.getContext().getAuthentication()){
OAuth2AuthenticationToken currentUser=(OAuth2AuthenticationToken)SecurityContextHolder.getContext()
.getAuthentication();
OAuth2AuthorizedClient authorizedClient=this.OAuth2AuthorizedClient服务
.loadAuthorizedClient(currentUser.GetAuthorizedClient注册ID(),currentUser.getName());
/**
*检查现有令牌是否过期。
*/
if(isExpired(authorizedClient.getAccessToken()){
/*
*做一些事情来获取新的访问令牌
*/
log.debug(
“=======================================================令牌已过期!!要刷新===========================================================”;
ClientRegistration ClientRegistration=authorizedClient.getClientRegistration();
/*
*调用身份验证服务器令牌端点以刷新令牌。
*/
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest=新OAuth2RefreshTokenGrantRequest(
clientRegistration、authorizedClient.getAccessToken()、authorizedClient.getRefreshToken());
OAuth2AccessTokenResponse accessTokenResponse=this.accessTokenResponseClient
.getTokenResponse(刷新令牌请求);
/*
*将id_令牌转换为OidcToken。
*/
OidcIdToken idToken=createOidcToken(clientRegistration,accessTokenResponse);
/*
*因为我已经实现了自定义的OidcUserService,所以重用现有的
*获取新用户的代码。
*/
OidcUser OidcUser=this.userService.loadUser(新的OidcUserRequest(clientRegistration,
accessTokenResponse.getAccessToken()、idToken、accessTokenResponse.getAdditionalParameters());
log.debug(
“=============================================================================================================”;
/*
*打印新旧id_令牌,以防万一。
*/
DefaultOidcUser user=(DefaultOidcUser)currentUser.getPrincipal();
debug(“新id标记为”+oidcUser.getIdToken().getTokenValue());
debug(“旧id标记为”+user.getIdToken().getTokenValue());
/*
*创建新的身份验证(OAuth2AuthenticationToken)。
*/
OAuth2AuthenticationToken UpdateUser=新的OAuth2AuthenticationToken(oidcUser,
oidcUser.GetAuthories(),currentUser.GetAuthorizedClient注册ID();
/*
*通过保存新的授权客户端来更新访问\u令牌和刷新\u令牌。
*/
OAuth2AuthorizedClient updatedAuthorizedClient=新的OAuth2AuthorizedClient(clientRegistration,
currentUser.getName(),accessTokenResponse.getAccessToken(),
accessTokenResponse.getRefreshToken());
this.oAuth2AuthorizedClientService.saveAuthorizedClient(updateauthorizedclient,updateuser);
/*
*在SecurityContextHolder中设置新身份验证。
*/
SecurityContextHolder.getContext().setAuthen
/**
*
* @author agam
*
*/
@Component
public class ExpiredTokenFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(ExpiredTokenFilter.class);
private Duration accessTokenExpiresSkew = Duration.ofMillis(1000);
private Clock clock = Clock.systemUTC();
@Autowired
private OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
@Autowired
CustomOidcUserService userService;
private DefaultRefreshTokenTokenResponseClient accessTokenResponseClient;
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory;
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
public ExpiredTokenFilter() {
super();
this.accessTokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
this.jwtDecoderFactory = new OidcIdTokenDecoderFactory();
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
log.debug("my custom filter called ");
/**
* check if authentication is done.
*/
if (null != SecurityContextHolder.getContext().getAuthentication()) {
OAuth2AuthenticationToken currentUser = (OAuth2AuthenticationToken) SecurityContextHolder.getContext()
.getAuthentication();
OAuth2AuthorizedClient authorizedClient = this.oAuth2AuthorizedClientService
.loadAuthorizedClient(currentUser.getAuthorizedClientRegistrationId(), currentUser.getName());
/**
* Check if token existing token is expired.
*/
if (isExpired(authorizedClient.getAccessToken())) {
/*
* do something to get new access token
*/
log.debug(
"=========================== Token Expired !! going to refresh ================================================");
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
/*
* Call Auth server token endpoint to refresh token.
*/
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(
clientRegistration, authorizedClient.getAccessToken(), authorizedClient.getRefreshToken());
OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseClient
.getTokenResponse(refreshTokenGrantRequest);
/*
* Convert id_token to OidcToken.
*/
OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
/*
* Since I have already implemented a custom OidcUserService, reuse existing
* code to get new user.
*/
OidcUser oidcUser = this.userService.loadUser(new OidcUserRequest(clientRegistration,
accessTokenResponse.getAccessToken(), idToken, accessTokenResponse.getAdditionalParameters()));
log.debug(
"=========================== Token Refresh Done !! ================================================");
/*
* Print old and new id_token, just in case.
*/
DefaultOidcUser user = (DefaultOidcUser) currentUser.getPrincipal();
log.debug("new id token is " + oidcUser.getIdToken().getTokenValue());
log.debug("old id token was " + user.getIdToken().getTokenValue());
/*
* Create new authentication(OAuth2AuthenticationToken).
*/
OAuth2AuthenticationToken updatedUser = new OAuth2AuthenticationToken(oidcUser,
oidcUser.getAuthorities(), currentUser.getAuthorizedClientRegistrationId());
/*
* Update access_token and refresh_token by saving new authorized client.
*/
OAuth2AuthorizedClient updatedAuthorizedClient = new OAuth2AuthorizedClient(clientRegistration,
currentUser.getName(), accessTokenResponse.getAccessToken(),
accessTokenResponse.getRefreshToken());
this.oAuth2AuthorizedClientService.saveAuthorizedClient(updatedAuthorizedClient, updatedUser);
/*
* Set new authentication in SecurityContextHolder.
*/
SecurityContextHolder.getContext().setAuthentication(updatedUser);
}
}
filterChain.doFilter(request, response);
}
private Boolean isExpired(OAuth2AccessToken oAuth2AccessToken) {
Instant now = this.clock.instant();
Instant expiresAt = oAuth2AccessToken.getExpiresAt();
return now.isAfter(expiresAt.minus(this.accessTokenExpiresSkew));
}
private OidcIdToken createOidcToken(ClientRegistration clientRegistration,
OAuth2AccessTokenResponse accessTokenResponse) {
JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);
Jwt jwt;
try {
jwt = jwtDecoder
.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN));
} catch (JwtException ex) {
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null);
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);
}
OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(),
jwt.getClaims());
return idToken;
}
}
@Bean
public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) {
AuthorizedClientServiceOAuth2AuthorizedClientManager manager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
manager.setAuthorizedClientProvider(new DelegatingOAuth2AuthorizedClientProvider(
new RefreshTokenOAuth2AuthorizedClientProvider(),
new ClientCredentialsOAuth2AuthorizedClientProvider()));
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(manager);
oauth2.setDefaultClientRegistrationId("your-client-registratioin-id");
return WebClient.builder()
.filter(oauth2)
.apply(oauth2.oauth2Configuration())
.build();
}
@Autowire
private final WebClient webClient;
...
webClient.get()
.uri("http://localhost:8081/api/message")
.retrieve()
.bodyToMono(String.class)
.map(string -> "Retrieved using password grant: " + string)
.subscribe(log::info);