Spring Security与OIDC:刷新令牌

fykwrbwg  于 2023-10-20  发布在  Spring
关注(0)|答案(4)|浏览(153)

Sping Boot 2 with Spring Security 5可以配置为使用openID connect ID provider进行身份验证。我设法通过配置Spring Security来设置我的项目-它可以很好地与各种完美的预配置安全机制一起工作,比如缓解会话固定。
但看起来Spring Security在令牌过期时不会自行刷新令牌(存储在会话中)。
有没有设置,或者我必须照顾自己的刷新?

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

u0sqgete

u0sqgete1#

根据记录
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;

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

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

String refreshToken = client.getRefreshToken();

而且,现在找不到它,但我假设作为OAuth2AuthorizedClientExchangeFilterFunction的一部分,它可以调用刷新。

pbgvytdp

pbgvytdp2#

根据https://github.com/spring-projects/spring-security/issues/6742,令牌似乎故意不刷新:
ID令牌通常带有到期日期。RPMAY依靠它使RP会话过期。
Spring没有。在最后提到了两个增强功能,它们应该可以解决一些刷新问题--这两个功能都还没有解决。
作为一种变通方法,我实现了一个 GenericFilterBean,它检查令牌并清除当前安全上下文中的身份验证。因此,需要一个新的token。

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

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

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

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

  • spring-boot-Starter-web
  • Spring启动器安全
  • spring-boot-starter-oauth2-client

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

bvjxkvbb

bvjxkvbb3#

如果有人到那里寻找新版本的Spring的答案。现在我们有Spring 6。我正在使用oauth2Login()功能登录,使用github,facebook等社交网站。我想将用户会话绑定到访问令牌的属性。如果过期,应刷新令牌并尝试延长会话,然后强制用户再次登录。Spring中的非React式解决方案(没有WebClientOAuth2AuthorizedClientExchangeFilterFunction)在oauth2Login()特性中不提供自动机制。但是我为OAuth2AuthorizedClientManager配置了一个bean,它可以与Spring reactive的oauth2Client()功能一起使用。但是,手动刷新访问令牌并在异常情况下手动清除身份验证上下文可能是一种很酷的方法。

@Bean
    public OAuth2AuthorizedClientManager authorizedClientManager() {
        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .authorizationCode()
                        .refreshToken()
                        .build();

        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository(), authorizedClientRepository());
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
        return authorizedClientManager;
    }

和过滤器

public class ExpiredTokenFilter extends OncePerRequestFilter {

    @Resource
    private OAuth2AuthorizedClientManager authorizedClientManager;

    @Resource
    private OAuth2AuthorizedClientRepository authorizedClientRepository;

    @Override
    protected void doFilterInternal(
            @Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response,
            @Nonnull FilterChain filterChain)
            throws ServletException, IOException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (Objects.nonNull(authentication) && authentication.getPrincipal() instanceof OidcUser) {
            validateToken(authentication, request, response);
        }

        filterChain.doFilter(request, response);
    }

    private void validateToken(Authentication auth, HttpServletRequest request, HttpServletResponse response) {
        OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
                .withClientRegistrationId(MY_PROVIDER)
                .principal(auth)
                .attributes(attrs -> {
                    attrs.put(HttpServletRequest.class.getName(), request);
                    attrs.put(HttpServletResponse.class.getName(), response);
                })
                .build();
        try {
            authorizedClientManager.authorize(authorizeRequest);
        } catch (ClientAuthorizationException e) {
            SecurityContextHolder.getContext().setAuthentication(null);
        }
    }
}

记住在SecurityFilterChain中注册Filter

http.addFilterBefore(expiredTokenFilter, AbstractPreAuthenticatedProcessingFilter.class));
6ju8rftf

6ju8rftf4#

100点声望值的悬赏都没有得到答案。所以我猜目前还没有实现自动刷新Spring Security访问令牌的机制。
一个有效的替代方案似乎是使用能够刷新令牌的spring Boot keycloak适配器。

相关问题