Spring Security 在Spring Cloud Gateway中使用Auth0,获取ID令牌时出现问题

f1tvaqid  于 2023-02-04  发布在  Spring
关注(0)|答案(1)|浏览(171)

我们在使用auth0 SDK获取ID令牌时遇到问题。我们有基于Spring Cloud Gateway的API Gateway(版本3.1.4),我们尝试使用您的auth0平台对用户进行身份验证,然后将交换路由到我们的微服务。为此,我们希望使用ID令牌并从中获取电子邮件,然后将此电子邮件传递到我们的微服务。我们通过点击oauth2/authorization/auth0端点登录,我们将被重定向到auth0登录页面,在该页面中提供凭据,然后重定向回应用程序。
当我们直接在API网关中配置端点并使用@AuthenticationPrincipal OidcUser user标记时,它可以工作,并且我们拥有完整的用户详细信息以及ID令牌。当我们将交换代理到不同的服务时,我们在请求中拥有授权标头,它只包含标头和签名部分,而ID令牌中没有有效载荷。
我们需要ID令牌中的有效负载来获取用户电子邮件,以便在微服务中将用户与内部DB进行Map。
您认为在这种情况下,什么是正确的工作流程?我们如何解决这个问题?
我们尝试使用我粘贴在下面的规则和操作,但它没有帮助我们。
我们的配置如下所示:

@Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {
        return http
            .csrf().disable()
            .authorizeExchange()
            .pathMatchers("/test").authenticated()
            .anyExchange().authenticated()
            .and().oauth2Login()
            .and().logout().logoutSuccessHandler(logoutSuccessHandler())
            .and().build();
    }

在网关配置的路由定位器中,我们有令牌中继过滤器。
我们的操作如下所示:

exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'http://test.{our_local_development_route}:8888';
  if (event.authorization) {
    api.idToken.setCustomClaim(`${namespace}/claims/email`, event.user.email);
    api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
  }
};

和规则:

function addEmailToAccessToken(user, context, callback) {
// This rule adds the authenticated user's email address to the access token.

  const namespace = 'http://test.{our_local_development_route}:8888';
  context.idToken[namespace + 'email'] = user.upn;
  context.accessToken[namespace + 'email'] = user.email;
  return callback(null, user, context);
}
00jrzges

00jrzges1#

我最近用Spring Cloud Gateway和Auth0创建了一个微服务架构。我写了我是如何在Auth0 blog上创建它的。这不是有趣的部分。有趣的部分是JHipster生成一个ReactiveJwtDecoder,如果某些声明在访问令牌中不可用,它将调用/userinfo端点。这样,在接入令牌被中继到下游微服务之前,接入令牌被丰富了身份信息。

@Bean
ReactiveJwtDecoder jwtDecoder(ReactiveClientRegistrationRepository registrations) {
    Mono<ClientRegistration> clientRegistration = registrations.findByRegistrationId("oidc");

    return clientRegistration
        .map(oidc ->
            createJwtDecoder(
                oidc.getProviderDetails().getIssuerUri(),
                oidc.getProviderDetails().getJwkSetUri(),
                oidc.getProviderDetails().getUserInfoEndpoint().getUri()
            )
        )
        .block();
}

private ReactiveJwtDecoder createJwtDecoder(String issuerUri, String jwkSetUri, String userInfoUri) {
    NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(jwkSetUri);
    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

    jwtDecoder.setJwtValidator(withAudience);

    return new ReactiveJwtDecoder() {
        @Override
        public Mono<Jwt> decode(String token) throws JwtException {
            return jwtDecoder.decode(token).flatMap(jwt -> enrich(token, jwt));
        }

        private Mono<Jwt> enrich(String token, Jwt jwt) {
            // Only look up user information if identity claims are missing
            if (jwt.hasClaim("given_name") && jwt.hasClaim("family_name")) {
                return Mono.just(jwt);
            }
            // Retrieve user info from OAuth provider if not already loaded
            return users.get(
                jwt.getSubject(),
                s -> {
                    WebClient webClient = WebClient.create();

                    return webClient
                        .get()
                        .uri(userInfoUri)
                        .headers(headers -> headers.setBearerAuth(token))
                        .retrieve()
                        .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
                        .map(userInfo ->
                            Jwt
                                .withTokenValue(jwt.getTokenValue())
                                .subject(jwt.getSubject())
                                .audience(jwt.getAudience())
                                .headers(headers -> headers.putAll(jwt.getHeaders()))
                                .claims(claims -> {
                                    String username = userInfo.get("preferred_username").toString();
                                    // special handling for Auth0
                                    if (userInfo.get("sub").toString().contains("|") && username.contains("@")) {
                                        userInfo.put("email", username);
                                    }
                                    // Allow full name in a name claim - happens with Auth0
                                    if (userInfo.get("name") != null) {
                                        String[] name = userInfo.get("name").toString().split("\\s+");
                                        if (name.length > 0) {
                                            userInfo.put("given_name", name[0]);
                                            userInfo.put("family_name", String.join(" ", Arrays.copyOfRange(name, 1, name.length)));
                                        }
                                    }
                                    claims.putAll(userInfo);
                                })
                                .claims(claims -> claims.putAll(jwt.getClaims()))
                                .build()
                        );
                }
            );
        }
    };
}

相关问题