此问题在此处已有答案:
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资源服务器。请建议我如何解决这两个问题。
1条答案
按热度按时间ijxebb2r1#
您的过滤器是否标注了
@Component
?这可以解释为什么它被调用了两次,因为Sping Boot 会自动向servlet容器注册任何作为过滤器的bean(请参见documentation)。因此,您可以设置一个注册Bean来禁用它:
默认情况下,没有排序信息的自定义过滤器Bean会自动添加到主servlet过滤器链的最后一个位置(实际上是最低优先级,就像您将应用默认顺序注解
@Order(Ordered.LOWEST_PRECEDENCE)
一样,请参见Order)。在调试级别,您应该在日志中看到过滤器添加到链中时的位置,类似于:
...在附加滤波器链中的4个位置中的4个位置处;触发过滤器:用户保存筛选器
关于第二个问题,如果您确定用户确实被保存了(即您后来在数据库中找到了它),那么实际上可能只是因为您的
getAll()
方法在您将来的调用完成之前被执行了。