I have two beans: CheckPassword
and CheckEmail
@Service("CheckPassword")
@Primary
public class CheckPassword implements CheckStringInterface {
private static final String regex_password = "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$";
@Override
public boolean isStringValid(String rawPassword) {
Pattern pattern = Pattern.compile(regex_password);
Matcher matcher = pattern.matcher(rawPassword);
return matcher.matches();
}
}
@Service("CheckEmail")
public class CheckEmail implements CheckStringInterface {
private static final String regex_password = "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,6}$";
@Override
public boolean isStringValid(String rawPassword) {
Pattern pattern = Pattern.compile(regex_password);
Matcher matcher = pattern.matcher(rawPassword);
return matcher.matches();
}
}
I use @Qualifier
to select the bean I need, but why is it that only password returns true
while email always returns false
?
@Qualifier("CheckPassword")
private final CheckStringInterface checkPasswordFormat;
@Qualifier("CheckEmail")
private final CheckStringInterface checkEmailFormat;
@Qualifier("EmailVerifyCode")
private final VerificationCodeManager verificationCodeManager;
@Value("${email_root}")
private String email_root;
private final SendMailTemplateService sendMailTemplateService;
private static final String template_verify_code_register = "templateVerifyCodeRegister";
private static String message_notification = "Use the code to verify this email: ";
@Transactional
public Integer register(RegisterDTO registerDTO, HttpServletRequest request){
if (userService.findByEmail(registerDTO.email()) == null) {
if (checkPasswordFormat.isStringValid(registerDTO.password()) == false
|| checkEmailFormat.isStringValid(registerDTO.email()) == false) {
log.info("Wrong Format {}", checkPasswordFormat.isStringValid(registerDTO.password()));
log.info("Wrong Format {}", checkEmailFormat.isStringValid(registerDTO.email()));
return 0; // Wrong format email or password
}
User user = new User();
user.setEmail(registerDTO.email());
user.setFullName(registerDTO.fullName());
user.setPassword(registerDTO.password());
user.setCreatedAt(Time.getTheTimeRightNow());
user.setUserVerifyCode(verificationCodeManager.generateCode());
user.setUserVerifyCodeExpirationTime(verificationCodeManager.codeExpiration());
log.info("------> RegisterService | register: register with email {}", user.getEmail());
createUser.create(user);
sendMail(user, user.getUserVerifyCode());
request.getSession().setAttribute("email", registerDTO.email());
return 1; // Create successful
} else {
return 2; // Email already exists
}
}
This is the data I use to test the API on Swagger:
{
"email": "[email protected]",
"fullName": "abcdABCD",
"password": "Password@1234"
}
I tried to change to another regex but it didn't help.
When I log it I see password always return true
because I input the right format but email will return false
with both right and wrong format.
The code posted in the question doesn't work for two main reasons:
CheckPassword
and CheckEmail
is made in a wrong wayI see two main problems in your regex email validation:
\\\\
before the .
character, which will make the regex expect a \
(because to write the backslash in java you need \\
so another two \\
will actually escape it) and will cause the dot to be interpreted as special character meaning "any character"With your current setup,
isStringValid("BONBON147852369@GMAIL\\.COM")
returns true
isStringValid("BONBON147852369@GMAIL\\ACOM")
returns true
isStringValid("bONBON147852369@GMAIL\\.COM")
returns false
To fix your code, you need to use only two backslashes (instead of 4) before the dot, so that you will escape the dot character and you also need to make your regex expect also undercase letters:
@Service("CheckEmail")
public class CheckEmail implements CheckStringInterface {
private static final String regex_email = "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$";
@Override
public boolean isStringValid(String rawEmail) {
// alternative to Pattern.CASE_INSENSITIVE is to include a-z interval in your regex
Pattern pattern = Pattern.compile(regex_email, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(rawEmail);
return matcher.matches();
}
}
The OP used constructor injection, but put @Qualifier
on fields declarations. This caused the annotation to be ignored and hence only the bean with @Primary
annotation was picked (the CheckPassword
one).
To solve the problem, you can either
@Qualifier
and @Autowired
on fields' declarations (but that is not a best practice)@Qualifier
(see the first answer here: Spring @Autowired and @Qualifier)