title: Spring Security WebFlux - body with Authentication
body: <p>I want to implement simple Spring Security WebFlux application.<br>
  I want to use JSON message like </p>
  
  <pre><code>{
     'username': 'admin', 
     'password': 'adminPassword'
  } 
  </code></pre>
  
  <p>in body (POST request to /signin) to sign in my app.  </p>
  
  <p>What did I do?</p>
  
  <p>I created this configuration</p>
  
  <pre><code>@Configuration
  @EnableWebFluxSecurity
  @EnableReactiveMethodSecurity(proxyTargetClass = true)
  public class WebFluxSecurityConfig {
  
      @Autowired
      private ReactiveUserDetailsService userDetailsService;
  
      @Autowired
      private ObjectMapper mapper;
  
      @Bean
      public PasswordEncoder passwordEncoder() {
          return new BCryptPasswordEncoder(11);
      }
  
      @Bean
      public ServerSecurityContextRepository securityContextRepository() {
          WebSessionServerSecurityContextRepository securityContextRepository =
                  new WebSessionServerSecurityContextRepository();
  
          securityContextRepository.setSpringSecurityContextAttrName("securityContext");
  
          return securityContextRepository;
      }
  
      @Bean
      public ReactiveAuthenticationManager authenticationManager() {
          UserDetailsRepositoryReactiveAuthenticationManager authenticationManager =
                  new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
  
          authenticationManager.setPasswordEncoder(passwordEncoder());
  
          return authenticationManager;
      }
  
      @Bean
      public AuthenticationWebFilter authenticationWebFilter() {
          AuthenticationWebFilter filter = new AuthenticationWebFilter(authenticationManager());
  
          filter.setSecurityContextRepository(securityContextRepository());
          filter.setAuthenticationConverter(jsonBodyAuthenticationConverter());
          filter.setRequiresAuthenticationMatcher(
                  ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/signin")
          );
  
          return filter;
      }
  
  
  
      @Bean
      public Function&lt;ServerWebExchange, Mono&lt;Authentication&gt;&gt; jsonBodyAuthenticationConverter() {
          return exchange -&gt; {
              return exchange.getRequest().getBody()
                      .cache()
                      .next()
                      .flatMap(body -&gt; {
                          byte[] bodyBytes = new byte[body.capacity()];
                          body.read(bodyBytes);
                          String bodyString = new String(bodyBytes);
                          body.readPosition(0);
                          body.writePosition(0);
                          body.write(bodyBytes);
  
                          try {
                              UserController.SignInForm signInForm = mapper.readValue(bodyString, UserController.SignInForm.class);
  
                              return Mono.just(
                                      new UsernamePasswordAuthenticationToken(
                                              signInForm.getUsername(),
                                              signInForm.getPassword()
                                      )
                              );
                          } catch (IOException e) {
                              return Mono.error(new LangDopeException("Error while parsing credentials"));
                          }
                      });
          };
      }
  
      @Bean
      public SecurityWebFilterChain securityWebFiltersOrder(ServerHttpSecurity httpSecurity,
                                                            ReactiveAuthenticationManager authenticationManager) {
          return httpSecurity
                  .csrf().disable()
                  .httpBasic().disable()
                  .logout().disable()
                  .formLogin().disable()
                  .securityContextRepository(securityContextRepository())
                  .authenticationManager(authenticationManager)
                  .authorizeExchange()
                      .anyExchange().permitAll()
                  .and()
                  .addFilterAt(authenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
                  .build();
      }
  
  }
  </code></pre>
  
  <p>BUT I use jsonBodyAuthenticationConverter() and it reads Body of the incoming request. Body can be read only once, so I have an error  </p>
  
  <pre><code>java.lang.IllegalStateException: Only one connection receive subscriber allowed.
  </code></pre>
  
  <p>Actually it's working but with exception (session is set in cookies). How can I remake it without this error?</p>
  
  <p>Now I only created something like:</p>
  
  <pre><code>@PostMapping("/signin")
  public Mono&lt;Void&gt; signIn(@RequestBody SignInForm signInForm, ServerWebExchange webExchange) {
      return Mono.just(signInForm)
                 .flatMap(form -&gt; {
                      UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
                              form.getUsername(),
                              form.getPassword()
                      );
  
                      return authenticationManager
                              .authenticate(token)
                              .doOnError(err -&gt; {
                                  System.out.println(err.getMessage());
                              })
                              .flatMap(authentication -&gt; {
                                  SecurityContextImpl securityContext = new SecurityContextImpl(authentication);
  
                                  return securityContextRepository
                                          .save(webExchange, securityContext)
                                          .subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));
                              });
                  });
      }
  </code></pre>
  
  <p>And removed <code>AuthenticationWebFilter</code> from config.</p>