Spring Security with oidc:刷新令牌

rdm*_*ler 12 spring-security jwt openid-connect refresh-token

带有 Spring Security 5 的 Spring Boot 2 可以配置为使用 openID 连接 ID 提供程序进行身份验证。我仅通过配置 Spring Security 就设法设置了我的项目 - 这适用于各种完美预配置的安全机制,例如缓解会话固定。

但是似乎 Spring Security 在令牌过期时不会自行刷新令牌(存储在会话中)。

是否有设置或我必须自己照顾刷新?

更新:Spring Boot 2.1 已经发布,所以是时候重新审视这个问题了。我仍然不知道 accessToken 现在是否可以自动刷新,或者我是否必须为此编写代码......

Dar*_*the 7

根据文档,

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#webclient

使用正确配置的 WebClient 时,如文档中所述,它将自动刷新。

Spring Security 将自动刷新过期的令牌(如果存在刷新令牌)

支持刷新令牌的功能矩阵也支持这一点。

https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Features-Matrix

Spring Security 5 上有一个较旧的博客,可以让您访问可以手动执行此操作的 bean,

Authentication authentication =
    SecurityContextHolder
        .getContext()
        .getAuthentication();

OAuth2AuthenticationToken oauthToken =
    (OAuth2AuthenticationToken) authentication;
Run Code Online (Sandbox Code Playgroud)

在 Spring 应用程序上下文中将有一个 OAuth2AuthorizedClientService 自动配置为 bean,因此您只需将它注入到您将使用它的任何地方。

OAuth2AuthorizedClient client =
    clientService.loadAuthorizedClient(
            oauthToken.getAuthorizedClientRegistrationId(),
            oauthToken.getName());

String refreshToken = client.getRefreshToken();
Run Code Online (Sandbox Code Playgroud)

而且,现在没能找到它,但我认为作为其中的一部分,OAuth2AuthorizedClientExchangeFilterFunction需要进行刷新。


小智 4

根据https://github.com/spring-projects/spring-security/issues/6742,似乎故意不刷新令牌:

ID 令牌通常带有到期日期。RP可以 依靠它来使 RP 会话过期。

春天没有。最后提到了两项增强功能,应该可以解决一些刷新问题 - 两者仍然处于开放状态。

作为解决方法,我实现了一个GenericFilterBean,它检查令牌并清除当前安全上下文中的身份验证。因此需要一个新的令牌。

@Configuration
public class RefreshTokenFilterConfig {

    @Bean
    GenericFilterBean refreshTokenFilter(OAuth2AuthorizedClientService clientService) {
        return new GenericFilterBean() {
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication != null && authentication instanceof OAuth2AuthenticationToken) {
                    OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
                    OAuth2AuthorizedClient client =
                            clientService.loadAuthorizedClient(
                                    token.getAuthorizedClientRegistrationId(),
                                    token.getName());
                    OAuth2AccessToken accessToken = client.getAccessToken();
                    if (accessToken.getExpiresAt().isBefore(Instant.now())) {
                        SecurityContextHolder.getContext().setAuthentication(null);
                    }
                }
                filterChain.doFilter(servletRequest, servletResponse);
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,我必须将过滤器添加到安全配置中:

@Bean
public WebSecurityConfigurerAdapter webSecurityConfigurer(GenericFilterBean refreshTokenFilter) {
    return new WebSecurityConfigurerAdapter() {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                   .addFilterBefore(refreshTokenFilter,  AnonymousAuthenticationFilter.class)
Run Code Online (Sandbox Code Playgroud)

在2.2.7.RELEASE版本中使用 spring-boot-starter-parent 和依赖项实现:

  • spring-boot-启动器-web
  • spring-boot-starter-安全性
  • spring-boot-starter-oauth2-客户端

我很欣赏关于这个解决方法的意见,因为我仍然不确定 Spring Boot 中是否真的需要这样的开销。