Spring 安全 5.2 密码流程

Spring Security 5.2 Password Flow

我正在尝试使用最新版本 Spring Security - 5.2 中的密码流程对用户进行身份验证。

docs seem to suggest 怎么做。

@Bean
public OAuth2AuthorizedClientManager passwordFlowAuthorizedClientManager(
        HttpClient httpClient,
        ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientRepository authorizedClientRepository) {

    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

    DefaultPasswordTokenResponseClient c = new DefaultPasswordTokenResponseClient();
    RestTemplate client = new RestTemplate(requestFactory);
    client.setMessageConverters(Arrays.asList(
            new FormHttpMessageConverter(),
            new OAuth2AccessTokenResponseHttpMessageConverter()));
    client.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
    c.setRestOperations(client);

    OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
                .password(configurer -> configurer.accessTokenResponseClient(c))
                .refreshToken()
                .build();

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

    authorizedClientManager.setContextAttributesMapper(authorizeRequest -> {
        Map<String, Object> contextAttributes = new HashMap<>();
        String username = authorizeRequest.getAttribute(OAuth2ParameterNames.USERNAME);
        String password = authorizeRequest.getAttribute(OAuth2ParameterNames.PASSWORD);
        contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
        contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
        return contextAttributes;
    });

    return authorizedClientManager;
}



我执行了请求,我可以看到在 HTTP header 中返回的访问令牌,但是没有填充 SecurityContext 并且 session 用户保持匿名。

String username = "joe";
String password = "joe";
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
ClientRegistration r = clientRegistrationRepository.findByRegistrationId("keycloak");

OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(r.getRegistrationId())
        .principal(authentication)
        .attributes(attrs -> {
            attrs.put(OAuth2ParameterNames.USERNAME, username);
            attrs.put(OAuth2ParameterNames.PASSWORD, password);
        })
        .build();
OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);

有什么想法吗?

进一步阅读文档后,我认为 Spring Security 5.2 中的 Oauth 2 密码流与授权流的支持方式不同。 Spring Security 5.2 具有对 http 客户端的密码流支持,它可以缓存授权请求并在令牌过期之前刷新令牌 - 但没有终端用户密码流支持,其中客户端将凭据代理到授权服务器。

当然,完全可以通过获取凭据来验证最终用户的身份,实施自定义 AuthenticationProvider,将凭据与授权服务器交换令牌,并 returns 持久保存到上下文。