title: Roles Hierarchy in Spring Webflux Security
body: <p>I have implemented Webflux security by implementing:</p>
  
  <ul>
  <li>ReactiveUserDetailsService</li>
  <li>ReactiveAuthenticationManager</li>
  <li>ServerSecurityContextRepository</li>
  </ul>
  
  <p>Now, I am trying to introduce RoleHierarchy following the docs here: <a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#authz-hierarchical-roles" rel="nofollow noreferrer">Role Hierarchy Docs</a></p>
  
  <p>I have a user with role USER but he is getting 403 Denied on hitting a controller annotated with GUEST role. Role hierarchy is: <strong>"ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST"</strong></p>
  
  <pre><code>@Configuration
  @EnableWebFluxSecurity
  @EnableReactiveMethodSecurity
  public class SecurityConfig {
  
      private final DaoAuthenticationManager reactiveAuthenticationManager;
  
      private final SecurityContextRepository securityContextRepository;
  
      private static final String ROLE_HIERARCHIES = "ROLE_ADMIN &gt; ROLE_USER ROLE_USER &gt; ROLE_GUEST";
  
      @Autowired
      public SecurityConfig(DaoAuthenticationManager reactiveAuthenticationManager,
          SecurityContextRepository securityContextRepository) {
          this.reactiveAuthenticationManager = reactiveAuthenticationManager;
          this.securityContextRepository = securityContextRepository;
      }
  
      @Bean
      public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
          return http
              .csrf().disable()
              .formLogin().disable()
              .httpBasic().disable()
              .authenticationManager(reactiveAuthenticationManager)
              .securityContextRepository(securityContextRepository)
              .authorizeExchange()
              .anyExchange().permitAll()
              .and()
              .logout().disable()
              .build();
      }
  
      @Bean(name = "roleHierarchy")
      public RoleHierarchy roleHierarchy() {
          RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
          roleHierarchy.setHierarchy(ROLE_HIERARCHIES);
          return roleHierarchy;
      }
  
      @Bean(name = "roleVoter")
      public RoleVoter roleVoter() {
          return new RoleHierarchyVoter(roleHierarchy());
      }
  }
  </code></pre>
  
  <hr>
  
  <pre><code>@Component
  public class DaoAuthenticationManager implements ReactiveAuthenticationManager {
  
      private final DaoUserDetailsService userDetailsService;
  
      private final Scheduler scheduler;
  
      @Autowired
      public DaoAuthenticationManager(DaoUserDetailsService userDetailsService,
          Scheduler scheduler) {
          Assert.notNull(userDetailsService, "userDetailsService cannot be null");
          this.userDetailsService = userDetailsService;
          this.scheduler = scheduler;
      }
  
      @Override
      public Mono&lt;Authentication&gt; authenticate(Authentication authentication) {
          final String username = authentication.getName();
          return this.userDetailsService.findByUsername(username)
              .publishOn(this.scheduler)
              .switchIfEmpty(
                  Mono.defer(() -&gt; Mono.error(new UsernameNotFoundException("Invalid Username"))))
              .map(u -&gt; new UsernamePasswordAuthenticationToken(u, u.getPassword(),
                  u.getAuthorities()));
      }
  }
  </code></pre>
  
  <hr>
  
  <pre><code>@Component
  public class SecurityContextRepository implements ServerSecurityContextRepository {
  
      private final DaoAuthenticationManager authenticationManager;
  
      @Autowired
      public SecurityContextRepository(DaoAuthenticationManager authenticationManager) {
          this.authenticationManager = authenticationManager;
      }
  
      @Override
      public Mono&lt;Void&gt; save(ServerWebExchange swe, SecurityContext sc) {
          throw new UnsupportedOperationException("Not supported yet.");
      }
  
      @Override
      public Mono&lt;SecurityContext&gt; load(ServerWebExchange swe) {
          ServerHttpRequest request = swe.getRequest();
          if (request.getHeaders().containsKey("userName") &amp;&amp;
              !Objects.requireNonNull(request.getHeaders().get("userName")).isEmpty()) {
              String userName = Objects.requireNonNull(swe
                  .getRequest()
                  .getHeaders()
                  .get("userName")).get(0);
  
              Authentication auth = new UsernamePasswordAuthenticationToken(userName,
                  Security.PASSWORD);
              return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
          } else {
              return Mono.empty();
          }
      }
  
  
  }
  </code></pre>
  
  <p>Anyway to get the role hierarchy thing working in Webflux security.</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>Controller:</p>
  
  <pre><code>@GetMapping
  @PreAuthorize("hasRole('USER')")
  public Mono&lt;Device&gt; getDevice(@RequestParam String uuid) {
      return deviceService.getDevice(uuid);
  }
  </code></pre>
  
  <p>Normal role authorization is working for me, whats not working is the hierarchy part.</p>