教程演示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() ); }
}
|
最后一步,我们需要ReactiveUserDetailsService实现根据用户名返回用户详细信息。@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)); }
}
|
启动应用程序。尝试访问任何上述配置的端点。该应用程序将自动将您重定向到登录页面。