I'm trying to build a user registration based on the tutorial from Amigoscode Youtube: Java Tutorial, Git Repository, but I make it reactive with webflux, postgresql and R2DBC.
Now I'm struggling on saving the user data into the database. If I do it like it is in the tutorial, where the return types of the registration methods are String, it wont save the data at all. So I changed the return types to ApplicationUser and put the save()
into the return, but now, if I call the endpoint with postman, it will save the data, but it will also return all the user data including the encrypted password to postman. I'm not sure how much of a problem this is but I would rather like to avoid this behaviour. What to do?
This is the version which saves to database, but returns all the data:
ApplicationUserService.java
@RequiredArgsConstructor
@Service
public class ApplicationUserService implements ReactiveUserDetailsService {
private final static String USER_NOT_FOUND_MSG = "User with email %s not found";
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public Mono<ApplicationUser> signUpUser(ApplicationUser applicationUser){
String encodedPassword = bCryptPasswordEncoder.encode(applicationUser.getPassword());
applicationUser.setPassword(encodedPassword);
return userRepository.findByEmail(applicationUser.getEmail())
// .flatMap(t -> { throw new IllegalStateException("Email already exists"); })
.switchIfEmpty(userRepository.save(applicationUser));
}
This version doesn't return the data, but also doesn't save to the database:
Needs changing return types from Mono<ApplicationUser>
to Mono<String>
in other functions.
ApplicationUserService.java
@RequiredArgsConstructor
@Service
public class ApplicationUserService implements ReactiveUserDetailsService {
private final static String USER_NOT_FOUND_MSG = "User with email %s not found";
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public Mono<String> signUpUser(ApplicationUser applicationUser){
String encodedPassword = bCryptPasswordEncoder.encode(applicationUser.getPassword());
applicationUser.setPassword(encodedPassword);
userRepository.findByEmail(applicationUser.getEmail())
.flatMap(t -> { throw new IllegalStateException("Email already exists"); })
.switchIfEmpty(userRepository.save(applicationUser));
return Mono.just("Sign up works, maybe");
}
This returns "Sign up works,maybe" and thats it. No data is saved.
Remaining functions
ApplicationUser.java
@Data
@NoArgsConstructor
@Table("users")
public class ApplicationUser implements UserDetails {
@Id
private long id;
private String username;
private String password;
private String email;
private UserRole userRole;
private boolean isAccountNonExpired = true;
private boolean locked = false;
private boolean isCredentialsNonExpired = true;
private boolean isEnabled = false;
public ApplicationUser(String username, String password, String email,UserRole userRole) {
this.username = username;
this.password = password;
this.email = email;
this.userRole = userRole;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(userRole.name());
return Collections.singletonList(authority);
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return username;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return isAccountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return locked;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return isCredentialsNonExpired;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return isEnabled;
}
}
UserRole.java
public enum UserRole {
USER,
ADMIN,
MODERATOR
}
UserRepository.java
@Repository
@Transactional(readOnly = true)
public interface UserRepository extends ReactiveCrudRepository<ApplicationUser, Long>{
Mono<ApplicationUser> findByEmail(String email);
}
RegistrationController.java
@RestController
@RequestMapping(path = "/registration")
@AllArgsConstructor
public class RegistrationController {
private RegistrationService registrationService;
@PostMapping
public Mono<ApplicationUser> register(@RequestBody RegistrationRequest request){
return registrationService.reg(request);
}
}
RegistrationService.java
@Data
@Service
@AllArgsConstructor
public class RegistrationService {
private EmailValidator emailValidator;
private ApplicationUserService applicationUserService;
public Mono<ApplicationUser> reg(RegistrationRequest request) {
Boolean isValidEmail = emailValidator.test(request.getEmail());
if(!isValidEmail) {
throw new IllegalStateException("Email not valid");
}
return applicationUserService.signUpUser(
new ApplicationUser(
request.getUsername(),
request.getPassword(),
request.getEmail(),
UserRole.USER
)
);
}
}
RegistrationRequest.java
@Data
@AllArgsConstructor
@ToString
public class RegistrationRequest {
private String username;
private String password;
private String email;
private UserRole userRole;
}