79315739

Date: 2024-12-29 14:09:41
Score: 0.5
Natty:
Report link

I'm revisiting this for the purpose of completion and posterity. @Roar's answer hints at the solution but excludes actuals a live project is likelier to entail. Building upon his submission, what currently works involves the following classes:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Autowired
    private JwtReactiveAuthenticationManager authenticationManager;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(request -> {
                    CorsConfiguration config = new CorsConfiguration();
                    // bunch of config
                    return config;
                }))
                .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(); // NOTE THIS
                })
                .addFilterAt(jwtAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
                .build();
    }

    private AuthenticationWebFilter jwtAuthenticationFilter() {
        var authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);

        authenticationWebFilter.setServerAuthenticationConverter(exchange -> {
            String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                var token = authHeader.substring(7);
                // System.out.println(token); // correctly logs
                // instance is passed to JwtReactiveAuthenticationManager#authenticate
                return Mono.just(new UsernamePasswordAuthenticationToken(token, token));
            }
            return Mono.empty();
        });

        return authenticationWebFilter;
    }
}
@Component
@AllArgsConstructor
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(user); // correctly logs

                return new UsernamePasswordAuthenticationToken(user, token,
                        Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
            });
    }
}

And for retrieval on the controller side,

@GetMapping("/{id}")
public ResponseEntity<Flux<SomeDocument>> fetchDocuments(Authentication auth, @PathVariable String id) {
    User user = (User) auth.getPrincipal();

    // work with user.getId()
    }

I believe you can jwtTokenProvider off some internet tutorial but the above successfully binds decoded user to reactive security context.

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @Roar's
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: I Want Answers