Eu tentei trabalhar com ambos ReactiveAuthenticationManager
e um WebFilter
. Em ambos os casos, o fluxo de execução é alguma forma disto:
@Component
@RequiredArgsConstructor
public class JwtReactiveAuthenticationManager implements ReactiveAuthenticationManager {
private final JwtTokenProvider jwtTokenProvider;
private final UserRepository userRepository;
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String token = authentication.getCredentials().toString();
if (!jwtTokenProvider.validateToken(token)) {
System.out.println("Invalid token");
return Mono.empty();
}
String username = jwtTokenProvider.getUsernameFromToken(token);
return userRepository.findByEmail(username)
.switchIfEmpty(Mono.error(new RuntimeException("User not found")))
.map(user -> {
System.out.println("hydrated user from token. File is JRAM");
System.out.println(user); // this prints user
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
user.getUsername(),
null,
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
);
return authenticationToken;
});
}
}
Conforme comentado acima, o token é decodificado e o usuário é hidratado corretamente. Acredito que o problema decorre da vinculação dele ao ReactiveSecurityContextHolder
. Tentei diferentes variações do abaixo:
SecurityContextImpl securityContext = new SecurityContextImpl(authentication);
Context context = ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext));
Em uma instância, tentei definir o contexto de segurança e retorná-lo ao mono. Em outra instância, tentei apenas definir a autenticação diretamente em ReactiveSecurityContextHolder.withAuthentication
. Nada feito. Quando estava trabalhando com ReactiveAuthenticationManager
, também tenteiServerSecurityContextRepository
@Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
String authHeader = swe.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String authToken = authHeader.substring(7);
Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
return authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
} else {
return Mono.empty();
}
}
Não ajudou. Tentar ler autenticação de ReactiveSecurityContextHolder
em um método mono subsequente falha com um NullPointerException
. Então, acho que a configuração quebra, mas não consigo depurar isso porque switchIfEmpty
e onErrorResume
apenas imprimir o que for dado a eles do mesmo jeito.
A maioria das respostas de estouro de pilha sobre o assunto estão desatualizadas, usando métodos obsoletos. Além disso, nenhuma delas funciona para mim ao recuperar o usuário. A autenticação provavelmente está falhando, mas em algum momento, eu consegui acessar o parâmetro do usuário, embora estivesse em branco. Outras vezes, a solicitação simplesmente recua com 401
Abaixo está o securityConfig
http
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
// bunch of stuff
return config;
}))
/*.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)*/
.authorizeExchange(authorizeExchangeSpec -> {
authorizeExchangeSpec.pathMatchers(
"api/v1/user/login",
"api/v1/user/register","webjars/**",
"/v3/api-docs/**", "/swagger-ui.html",
"/swagger-ui/**").permitAll();
authorizeExchangeSpec.anyExchange().authenticated();
})
.addFilterAt(jwtAuthenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
.build();
Por favor, ajude a soletrar as etapas necessárias para passar meu usuário para o contexto de segurança e recuperá-lo dos métodos do controlador. Estou no spring boot 3.3.4. Obrigado