I use reCAPTCHA in my Java application and there is an approach similar to the following code:
LoginRequest:
public class LoginRequest {
@NotEmpty
private String token;
@NotEmpty
private String username;
@NotEmpty
private String password;
}
LoginController :
public class LoginController {
private final Validator validator;
@Value("${isDevelopment}")
private boolean isDevelopment;
public LoginController(Validator validator) {
this.validator = validator;
}
public User login(LoginRequest request) {
if(isDevelopment || validator.isValid(request.token)) {
// ...
}
throw new InvalidTokenException();
}
}
In this approach, I thought to bypass validation using the application development environment variable isDevelopment
. However, I am not sure if there is a more elegant way. Because reCAPTCHA
is used widely and most probably there would be a better standards for this purpose. How can I bypass reCAPTCHA during development?
Update: Here is my Validator
class:
@Component
@RequiredArgsConstructor
public class CaptchaValidator {
private final RecaptchaEnterpriseServiceClient client;
@Value("${enabled}")
private boolean isEnabled;
public boolean isValid(String token) {
// code omitted
}
}
Technically, such design is not wrong - this kind of approach is being used widely. It is named Feature Flags and there are 3rd party software solutions that help developers to work with such variables (like e.g. LaunchDarkly).
From Objective Programming perspective, you can also deal with this situation by creating some wrapper interface (let say UserValidator
), injecting this to LoginController
...
public class LoginController {
private final UserValidator validator;
public LoginController(UserValidator validator) {
this.validator = validator;
}
// ...
public User login(LoginRequest request) {
if(validator.validate(request.token)) {
// ...
...making your CaptchaValidator
implementing this but also creating some dev
stub for the interface (let say AlwaysTrueValidator
). Then during the development you can use your stub and just change configuration when going live. You can even take some advantage of using Spring profiles.
@Configuration
@Profile("production")
public class ProductionConfiguration {
@Bean
public LoginController loginController() {
return new LoginController(new CaptchaValidator());
}
}
// ...
@Configuration
@Profile("development")
public class DevelopmentConfiguration {
@Bean
public LoginController loginController() {
return new LoginController(new AlwaysTrueValidator());
}
}