使用 Spring Security 5 OAuth2.0 动态配置客户端注册范围

问题描述 投票:0回答:3

我想动态更改客户端注册的范围。 我知道如何以这种方式设置注册:

spring:
  security:
    oauth2:
      client:
        registration:
          custom:
            client-id: clientId
            client-secret: clientSecret
            authorization-grant-type: client_credentials
        provider:
          custom:
            token-uri: http://localhost:8081/oauth/token

如何以编程方式配置此功能?

spring spring-boot spring-security oauth-2.0 spring-webclient
3个回答
4
投票

您需要提供自定义

ClientRegistrationRepository
bean。它在docs中进行了描述。

@Configuration
public class OAuth2LoginConfig {

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(this.googleClientRegistration());
    }

    private ClientRegistration googleClientRegistration() {
        return ClientRegistration.withRegistrationId("google")
            .clientId("google-client-id")
            .clientSecret("google-client-secret")
            .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")
            .scope("openid", "profile", "email", "address", "phone")
            .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
            .tokenUri("https://www.googleapis.com/oauth2/v4/token")
            .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
            .userNameAttributeName(IdTokenClaimNames.SUB)
            .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
            .clientName("Google")
            .build();
    }
}

2
投票

免责声明:这是一种hacky方法,但它有效

(如果它很愚蠢但有效,那么它并不愚蠢)

所有相关部分将始终使用

ClientRegistrationRepository
来查找 ClientRegistration(以及范围)。

所以我解决这个问题的黑客方法是围绕

InMemoryClientRegistrationRepository
构建一个包装器。就我而言,我希望允许客户端请求任何其他范围,因此我想从查询参数添加其他范围
scope

这是此解决方案的示例代码:

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository(OAuth2ClientProperties properties) {
        final List<ClientRegistration> registrations = new ArrayList<>(OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
        // this is the ClientRegistrationRepository that would be used by default configuration
        final ClientRegistrationRepository parent = new InMemoryClientRegistrationRepository(registrations);

        // this lambda is our wrapper around the configuration based ClientRegistrationRepository
        return (registrationId) -> {
            final ClientRegistration clientRegistration = parent.findByRegistrationId(registrationId);
            if (clientRegistration == null) {
                return null;
            }

            final HttpServletRequest request = Optional.ofNullable(RequestContextHolder.getRequestAttributes())
                    .filter(ServletRequestAttributes.class::isInstance)
                    .map(ServletRequestAttributes.class::cast)
                    .map(ServletRequestAttributes::getRequest)
                    .orElse(null);

            final String query;
            if (request == null || (query = request.getQueryString()) == null) {
                return clientRegistration;
            }

            final List<String> scopeQueryParam = parseQuery(query).get(OAuth2ParameterNames.SCOPE);
            if (scopeQueryParam == null) {
                return clientRegistration;
            }

            final Set<String> scopes = scopeQueryParam.stream()
                    .flatMap((v) -> Arrays.stream(v.split(" ")))
                    .collect(Collectors.toSet());

            if (clientRegistration.getScopes().containsAll(scopes)) {
                return clientRegistration;
            }

            final Set<String> resultingScopes = new HashSet<>(scopes);
            resultingScopes.addAll(clientRegistration.getScopes());

            return ClientRegistration.withClientRegistration(clientRegistration)
                    .scope(resultingScopes)
                    .build();
        };
    }

    private static MultiValueMap<String, String> parseQuery(String query) {
        final MultiValueMap<String, String> result = new LinkedMultiValueMap<>();

        final String[] pairs = query.split("&");
        String[] pair;

        for (String _pair : pairs) {
            pair = _pair.split("=");

            if (pair.length >= 1) {
                final List<String> values = result.computeIfAbsent(URLDecoder.decode(pair[0], StandardCharsets.UTF_8), (k) -> new ArrayList<>());

                if (pair.length >= 2) {
                    values.add(URLDecoder.decode(pair[1], StandardCharsets.UTF_8));
                }
            }
        }

        return result;
    }

0
投票

我已在 Azure 门户上注册了我的应用程序。我需要知道客户名称和注册 ID 之间的区别。 @matejko219 你能帮助我找到我的应用程序的registrationId吗?

© www.soinside.com 2019 - 2024. All rights reserved.