12.5.2 Cấu hình một dịch vụ chi tiết người dùng phản ứng
Khi mở rộng WebSecurityConfigurerAdapter, bạn ghi đè một phương thức configure() để khai báo các quy tắc bảo mật web và một phương thức configure() khác để cấu hình logic xác thực, thường là bằng cách định nghĩa một đối tượng UserDetails. Để nhắc lại cách điều này hoạt động, hãy xem xét phương thức configure() được ghi đè dưới đây, sử dụng một đối tượng UserRepository được tiêm vào trong một triển khai ẩn danh của UserDetailsService để tra cứu người dùng theo tên người dùng:
@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();
}
});
}Trong cấu hình không phản ứng này, bạn ghi đè phương thức duy nhất được yêu cầu bởi UserDetailsService: loadUserByUsername(). Bên trong phương thức đó, bạn sử dụng UserRepository được cung cấp để tra cứu người dùng dựa trên tên người dùng được truyền vào. Nếu không tìm thấy tên, bạn ném ra một ngoại lệ UsernameNotFoundException. Nhưng nếu tìm thấy, bạn gọi một phương thức trợ giúp, toUserDetails(), để trả về đối tượng UserDetails tương ứng.
Trong cấu hình bảo mật phản ứng, bạn không ghi đè bất kỳ phương thức configure() nào. Thay vào đó, bạn khai báo một bean ReactiveUserDetailsService. ReactiveUserDetailsService là phiên bản phản ứng của UserDetailsService. Giống như UserDetailsService, ReactiveUserDetailsService chỉ yêu cầu triển khai một phương thức duy nhất. Cụ thể, phương thức findByUsername() trả về một Mono<UserDetails> thay vì một đối tượng UserDetails thô.
Trong ví dụ sau, bean ReactiveUserDetailsService được khai báo để sử dụng một UserRepository đã cho, được giả định là một repository Spring Data phản ứng (chúng ta sẽ bàn thêm về điều này ở chương tiếp theo):
@Service
@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();
});
}
};
}Tại đây, một Mono<UserDetails> được trả về như yêu cầu, nhưng phương thức UserRepository.findByUsername() lại trả về một Mono<User>. Vì nó là một Mono, bạn có thể nối chuỗi các thao tác trên đó, chẳng hạn như thao tác map() để ánh xạ Mono<User> thành Mono<UserDetails>.
Trong trường hợp này, thao tác map() được áp dụng với một biểu thức lambda gọi phương thức trợ giúp toUserDetails() trên đối tượng User được phát ra bởi Mono. Điều này chuyển đổi User thành UserDetails. Do đó, thao tác .map() trả về một Mono<UserDetails>, chính xác là những gì phương thức ReactiveUserDetailsService.findByUsername() yêu cầu. Nếu findByUsername() không thể tìm thấy người dùng phù hợp, thì Mono được trả về sẽ rỗng, biểu thị không có kết quả khớp và dẫn đến thất bại trong việc xác thực.
