Erweiterungen
This commit is contained in:
@@ -11,21 +11,39 @@ import com.vaadin.flow.component.textfield.PasswordField;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.VaadinSession;
|
||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||
import de.assecutor.votianlt.pages.service.UserService;
|
||||
import de.assecutor.votianlt.util.MailUtil;
|
||||
import jakarta.mail.MessagingException;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Route("register")
|
||||
@PageTitle("Bei VotianLT registrieren")
|
||||
@AnonymousAllowed
|
||||
public class RegisterView extends VerticalLayout {
|
||||
private final UserService userService;
|
||||
private final MailUtil mailUtil;
|
||||
|
||||
private TextField emailField;
|
||||
private PasswordField passwordField;
|
||||
private PasswordField confirmPasswordField;
|
||||
private TextField codeField;
|
||||
private Button submitButton;
|
||||
private Button verifyButton;
|
||||
private Button resendButton;
|
||||
|
||||
public RegisterView(UserService userService) {
|
||||
private String pendingCode; // 6-stelliger Code im View-Zustand
|
||||
private LocalDateTime codeExpiresAt;
|
||||
private LocalDateTime lastSentAt;
|
||||
private boolean awaitingVerification = false;
|
||||
|
||||
public RegisterView(UserService userService, MailUtil mailUtil) {
|
||||
this.userService = userService;
|
||||
this.mailUtil = mailUtil;
|
||||
// Layout-Konfiguration für vollständige Zentrierung
|
||||
setSizeFull();
|
||||
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||
@@ -80,22 +98,46 @@ public class RegisterView extends VerticalLayout {
|
||||
confirmPasswordField.setRequired(true);
|
||||
confirmPasswordField.setPlaceholder("Passwort wiederholen");
|
||||
|
||||
// Submit Button
|
||||
submitButton = new Button("Registrieren", event -> registerUser());
|
||||
codeField = new TextField("Bestätigungscode (6 Ziffern)");
|
||||
codeField.setWidthFull();
|
||||
codeField.setMaxLength(6);
|
||||
codeField.setPattern("\\d{6}");
|
||||
codeField.setPlaceholder("z. B. 123456");
|
||||
codeField.setVisible(false);
|
||||
codeField.addValueChangeListener(e -> {
|
||||
String v = e.getValue();
|
||||
if (v != null && !v.matches("\\d*")) {
|
||||
codeField.setValue(v.replaceAll("[^0-9]", ""));
|
||||
}
|
||||
});
|
||||
|
||||
// Buttons
|
||||
submitButton = new Button("Registrieren", event -> onStartRegistration());
|
||||
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_LARGE);
|
||||
submitButton.setWidthFull();
|
||||
|
||||
verifyButton = new Button("Code prüfen und registrieren", event -> onVerifyCode());
|
||||
verifyButton.addThemeVariants(ButtonVariant.LUMO_SUCCESS);
|
||||
verifyButton.setWidthFull();
|
||||
verifyButton.setVisible(false);
|
||||
|
||||
resendButton = new Button("Code erneut senden", event -> onResendCode());
|
||||
resendButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||
resendButton.setWidthFull();
|
||||
resendButton.setVisible(false);
|
||||
|
||||
// Zurück-Link
|
||||
Button backButton = new Button("Zurück zur Startseite", event ->
|
||||
getUI().ifPresent(ui -> ui.navigate("")));
|
||||
backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||
backButton.setWidthFull();
|
||||
|
||||
container.add(title, subtitle, emailField, passwordField, confirmPasswordField, submitButton, backButton);
|
||||
container.add(title, subtitle, emailField, passwordField, confirmPasswordField,
|
||||
submitButton, codeField, verifyButton, resendButton, backButton);
|
||||
return container;
|
||||
}
|
||||
|
||||
private void registerUser() {
|
||||
private void onStartRegistration() {
|
||||
var email = emailField.getValue().trim();
|
||||
var password = passwordField.getValue();
|
||||
var confirmPassword = confirmPasswordField.getValue();
|
||||
@@ -106,50 +148,112 @@ public class RegisterView extends VerticalLayout {
|
||||
emailField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email.contains("@") || !email.contains(".")) {
|
||||
Notification.show("Bitte geben Sie eine gültige E-Mail-Adresse ein.", 3000, Notification.Position.MIDDLE);
|
||||
emailField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (userService.existsByEmail(email)) {
|
||||
Notification.show("Ein Benutzer mit dieser E-Mail-Adresse existiert bereits.", 4000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
if (password.isEmpty()) {
|
||||
Notification.show("Bitte geben Sie ein Passwort ein.", 3000, Notification.Position.MIDDLE);
|
||||
passwordField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length() < 6) {
|
||||
Notification.show("Das Passwort muss mindestens 6 Zeichen lang sein.", 3000, Notification.Position.MIDDLE);
|
||||
passwordField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!password.equals(confirmPassword)) {
|
||||
Notification.show("Die Passwörter stimmen nicht überein.", 3000, Notification.Position.MIDDLE);
|
||||
confirmPasswordField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Alles ok: Code erzeugen und senden
|
||||
sendVerificationCode(email);
|
||||
}
|
||||
|
||||
private void sendVerificationCode(String email) {
|
||||
// Rate-Limit: 60 Sekunden zwischen Sendungen
|
||||
if (lastSentAt != null && Duration.between(lastSentAt, LocalDateTime.now()).getSeconds() < 60) {
|
||||
long wait = 60 - Duration.between(lastSentAt, LocalDateTime.now()).getSeconds();
|
||||
Notification.show("Bitte warten Sie " + wait + " Sekunden, bevor Sie den Code erneut senden.", 4000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
String code = generateSixDigitCode();
|
||||
pendingCode = code;
|
||||
codeExpiresAt = LocalDateTime.now().plusMinutes(10);
|
||||
lastSentAt = LocalDateTime.now();
|
||||
|
||||
String subject = "Ihr VotianLT Bestätigungscode";
|
||||
String body = "Ihr Bestätigungscode lautet: " + code + "\n\n" +
|
||||
"Dieser Code ist 10 Minuten gültig.\n" +
|
||||
"Wenn Sie diese Registrierung nicht angefragt haben, ignorieren Sie diese E-Mail.";
|
||||
try {
|
||||
// Benutzer erstellen
|
||||
userService.createUser(email, password, "Benutzer", "Name");
|
||||
mailUtil.sendMail(email, subject, body);
|
||||
awaitingVerification = true;
|
||||
// UI umstellen: Code-Eingabe anzeigen
|
||||
codeField.clear();
|
||||
codeField.setVisible(true);
|
||||
verifyButton.setVisible(true);
|
||||
resendButton.setVisible(true);
|
||||
|
||||
// Erfolgsmeldung und Weiterleitung
|
||||
Notification.show("Registrierung erfolgreich! Sie werden zur Anmeldung weitergeleitet.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
emailField.setReadOnly(true);
|
||||
passwordField.setReadOnly(true);
|
||||
confirmPasswordField.setReadOnly(true);
|
||||
submitButton.setEnabled(false);
|
||||
|
||||
getUI().ifPresent(ui -> ui.navigate("login"));
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
if (e.getMessage().contains("already exists")) {
|
||||
Notification.show("Ein Benutzer mit dieser E-Mail-Adresse existiert bereits.",
|
||||
5000, Notification.Position.MIDDLE);
|
||||
emailField.focus();
|
||||
} else {
|
||||
Notification.show("Registrierung fehlgeschlagen: " + e.getMessage(),
|
||||
5000, Notification.Position.MIDDLE);
|
||||
}
|
||||
Notification.show("Ein Bestätigungscode wurde an " + email + " gesendet.", 4000, Notification.Position.MIDDLE);
|
||||
} catch (MessagingException e) {
|
||||
awaitingVerification = false;
|
||||
Notification.show("Fehler beim Senden der E-Mail: " + e.getMessage(), 5000, Notification.Position.MIDDLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void onVerifyCode() {
|
||||
if (!awaitingVerification) {
|
||||
Notification.show("Bitte starten Sie zuerst die Registrierung.", 3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
String entered = codeField.getValue() != null ? codeField.getValue().trim() : "";
|
||||
if (!entered.matches("\\d{6}")) {
|
||||
Notification.show("Bitte geben Sie den 6-stelligen Code ein.", 3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
if (codeExpiresAt == null || LocalDateTime.now().isAfter(codeExpiresAt)) {
|
||||
Notification.show("Der Code ist abgelaufen. Bitte senden Sie einen neuen Code.", 4000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
if (!entered.equals(pendingCode)) {
|
||||
Notification.show("Der eingegebene Code ist ungültig.", 3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Code korrekt -> Benutzer erstellen
|
||||
var email = emailField.getValue().trim();
|
||||
var password = passwordField.getValue();
|
||||
try {
|
||||
userService.createUser(email, password, "Benutzer", "Name");
|
||||
VaadinSession.getCurrent().setAttribute("flashMessage", "Registrierung erfolgreich. Bitte melden Sie sich an.");
|
||||
getUI().ifPresent(ui -> ui.navigate("login"));
|
||||
} catch (RuntimeException e) {
|
||||
Notification.show("Registrierung fehlgeschlagen: " + e.getMessage(), 5000, Notification.Position.MIDDLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void onResendCode() {
|
||||
if (emailField.isReadOnly()) {
|
||||
sendVerificationCode(emailField.getValue().trim());
|
||||
}
|
||||
}
|
||||
|
||||
private String generateSixDigitCode() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
int num = random.nextInt(1_000_000); // 0..999999
|
||||
return String.format("%06d", num);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user