spring-security 验证成功后保存用户[复制]

j8yoct9x  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(159)

此问题在此处已有答案

Filter invoke twice when register as Spring bean(2个答案)
三个月前关门了。
我已经用google身份提供者设置了keycloak。我已经用spring security和MongoDB设置了一个简单的React式spring-boot web应用程序。我想在用户成功通过授权过滤器后保存他们。下面是我的安全配置:

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Slf4j
@RequiredArgsConstructor
public class SecurityConfiguration {

    private final UserSavingFilter userSavingFilter;

    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange()
                .anyExchange().authenticated()
                .and()
                .addFilterAfter(userSavingFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .oauth2ResourceServer()
                .jwt();

        return http.build();
    }

}

下面是我的保存用户的过滤器:

public class UserSavingFilter implements WebFilter {

    private final ObjectMapper objectMapper;

    private final UserService userService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        Base64.Decoder decoder = Base64.getUrlDecoder();
        var authHeader = getAuthHeader(exchange);
        if (authHeader == null) {
            return chain.filter(exchange);
        }
        var encodedPayload = authHeader.split("Bearer ")[1].split("\\.")[1];
        var userDetails = convertToMap(new String(decoder.decode(encodedPayload)));
        saveUserIfNotPresent(userDetails);

        return chain.filter(exchange);
    }

    @SneakyThrows
    private void saveUserIfNotPresent(Map<String, Object> map) {
        var userEmail = String.valueOf(map.get("email"));

        var userPresent = userService.existsByEmail(userEmail).toFuture().get();

        if (userPresent) {
            return;
        }

        log.info("Saving new user with email: {}", userEmail);

        var user = new User();
        user.setEmail(userEmail);
        user.setFirstName(String.valueOf(map.get("given_name")));
        user.setLastName(String.valueOf(map.get("family_name")));
        userService.save(user).subscribe(User::getId);
    }

    @SuppressWarnings("java:S2259")
    private String getAuthHeader(ServerWebExchange exchange) {
        var authHeaders = exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION);
        if (authHeaders == null) {
            return null;
        }
        return authHeaders.get(0);
    }

    @SneakyThrows
    private Map<String, Object> convertToMap(String payloadJson) {
        return objectMapper.readValue(payloadJson,Map.class);
    }
}

问题:
1.由于某种原因,我的过滤器每次请求执行两次。我可以看到2条关于保存新用户的日志消息。
1.当我调用getAll()端点时,它并不返回保存在这个请求中的用户筛选器。
也许这不是保存用户的最好方法,但是我找不到一个替代successHandler的jwt资源服务器。请建议我如何解决这两个问题。

ijxebb2r

ijxebb2r1#

您的过滤器是否标注了@Component?这可以解释为什么它被调用了两次,因为Sping Boot 会自动向servlet容器注册任何作为过滤器的bean(请参见documentation)。
因此,您可以设置一个注册Bean来禁用它:

@Bean
public FilterRegistrationBean<UserSavingFilter> disableUserSavingFilter(final UserSavingFilter filter) {
    final FilterRegistrationBean<UserSavingFilter> filterRegistrationBean = new FilterRegistrationBean<>();
    filterRegistrationBean.setFilter(filter);
    filterRegistrationBean.setEnabled(false);
    return filterRegistrationBean;
}

默认情况下,没有排序信息的自定义过滤器Bean会自动添加到主servlet过滤器链的最后一个位置(实际上是最低优先级,就像您将应用默认顺序注解@Order(Ordered.LOWEST_PRECEDENCE)一样,请参见Order)。
在调试级别,您应该在日志中看到过滤器添加到链中时的位置,类似于:
...在附加滤波器链中的4个位置中的4个位置处;触发过滤器:用户保存筛选器
关于第二个问题,如果您确定用户确实被保存了(即您后来在数据库中找到了它),那么实际上可能只是因为您的getAll()方法在您将来的调用完成之前被执行了。

相关问题