Spring WebFlux安全配置教程和源码 - vinsguru


教程演示Spring WebFlux Security反应式Web应用程序的安全性。
假设一个具有3个API端点的简单应用程序。我们需要具有如下所示的安全性。 

路径:/home/admin   角色:ADMIN
路径:/home/user    角色:ADMIN, USER
路径:/home/any     角色:ADMIN

 
第一步:
我使用以下端点创建一个REST控制器:

@RestController
@RequestMapping("home")
public class AuthController {

    @GetMapping(
"user")
    public Mono<String> userHome(){
        return Mono.just(
"user home");
    }

    @GetMapping(
"admin")
    public Mono<String> adminHome(){
        return Mono.just(
"admin home");
    }

    @GetMapping(
"any")
    public Mono<String> any(){
        return Mono.just(
"authenticated home");
    }

}

Spring WebFlux安全配置:
  • 创建一个配置类,以使用允许的角色/权限配置路径,如下所示。
  • 只要经过身份验证,应用程序中的任何人都可以使用任何其他路径(例如:/ home / any)。

@EnableWebFluxSecurity
public class WebSecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange()
                .pathMatchers("/home/user").hasAnyRole("USER", "ADMIN")
                .pathMatchers(
"/home/admin").hasRole("ADMIN")
                .anyExchange()
                .authenticated()
                .and()
                .formLogin();
        return http.build();
    }

}

@EnableReactiveMethodSecurity
如果您不喜欢上面的配置路径匹配器和角色方法,我们可以使安全性保持简单,如下所示。

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange()
                .anyExchange()
                .authenticated()
                .and()
                .formLogin();
        return http.build();
    }

}

我们可以在方法上添加PreAuthorize / PostAuthorize注释以提供类似的行为。
@GetMapping("user")
@PreAuthorize(
"hasRole('USER')")
public Mono<String> userHome(){
    return Mono.just(
"user home");
}

@GetMapping(
"admin")
@PreAuthorize(
"hasRole('ADMIN')")
public Mono<String> adminHome(){
    return Mono.just(
"admin home");
}


 
第二步 用户数据库:
我需要一个用户数据库。我正在将Map用于此演示目的。

  • 我们需要使用用户名,密码和用户特定角色来构建UserDetails对象

@Configuration
public class UserDB {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    public Map<String, UserDetails> map(){
        return Map.of(
                "user", User.withUsername("user").password(passwordEncoder.encode("user")).roles("USER").build(),
               
"admin", User.withUsername("admin").password(passwordEncoder.encode("admin")).roles("ADMIN").build(),
               
"any", User.withUsername("any").password(passwordEncoder.encode("any")).authorities(Collections.emptyList()).build()
        );
    }

}

 
最后一步,我们需要ReactiveUserDetailsS​​ervice实现根据用户名返回用户详细信息。

@Service
public class UserDetailsServiceImpl implements ReactiveUserDetailsService {

    @Autowired
    private Map<String, UserDetails> map;

    @Override
    public Mono<UserDetails> findByUsername(String username) {
       return Mono.just(this.map.get(username));
    }

}

  
启动应用程序。尝试访问任何上述配置的端点。该应用程序将自动将您重定向到登录页面。