在reactive的WebFlux Spring中若要設定security,有辦法直接使用Spring Security的類別嗎? 答案是否,因為Spring Security的前身為Acegi Security,是基於Servlet filter的架構來設計的,這在servlet中也很合理,在一個request進來以前透過filter來做security的邏輯,有通過才繼續下去。
那如果要secure WebFlux怎麼辦? 在Spring Security 5以後就有支援WebFlux了,而其介面的設計概念就是仿照Spring MVC那套來做。
在Spring MVC我們會設置如下的security config:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api", "/orders").hasAuthority("USER")
.antMatchers("/**").permitAll();
}
}
而在WebFlux會如下設置:
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/api", "/orders").hasAuthority("USER")
.anyExchange().permitAll()
.and()
.build();
}
}
那UserDetails呢? 先來回顧一下Spring MVC的寫法:
@Autowired
UserRepository userRepo;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth
.userDetailsService(new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepo.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username " + not found");
}
return user.toUserDetails();
}
});
}
而在WebFlux:
@Bean
public ReactiveUserDetailsService userDetailsService(UserRepository userRepo) {
return new ReactiveUserDetailsService() {
@Override
public Mono<UserDetails> findByUsername(String username) {
return userRepo.findByUsername(username)
.map(user -> {
return user.toUserDetails();
});
}
};
}
說實在的差別只在於一個回傳UserDetails,一個是回傳Mono,不過要進行reactive的coding通常就是透過Mono或Flux。