Erweiterungen

This commit is contained in:
2025-09-09 19:19:58 +02:00
parent bead579d4a
commit 8ecd05b3c0

View File

@@ -11,21 +11,39 @@ import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.VaadinSession;
import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.flow.server.auth.AnonymousAllowed;
import de.assecutor.votianlt.pages.service.UserService; 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") @Route("register")
@PageTitle("Bei VotianLT registrieren") @PageTitle("Bei VotianLT registrieren")
@AnonymousAllowed @AnonymousAllowed
public class RegisterView extends VerticalLayout { public class RegisterView extends VerticalLayout {
private final UserService userService; private final UserService userService;
private final MailUtil mailUtil;
private TextField emailField; private TextField emailField;
private PasswordField passwordField; private PasswordField passwordField;
private PasswordField confirmPasswordField; private PasswordField confirmPasswordField;
private TextField codeField;
private Button submitButton; 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.userService = userService;
this.mailUtil = mailUtil;
// Layout-Konfiguration für vollständige Zentrierung // Layout-Konfiguration für vollständige Zentrierung
setSizeFull(); setSizeFull();
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER); setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
@@ -80,22 +98,46 @@ public class RegisterView extends VerticalLayout {
confirmPasswordField.setRequired(true); confirmPasswordField.setRequired(true);
confirmPasswordField.setPlaceholder("Passwort wiederholen"); confirmPasswordField.setPlaceholder("Passwort wiederholen");
// Submit Button codeField = new TextField("Bestätigungscode (6 Ziffern)");
submitButton = new Button("Registrieren", event -> registerUser()); 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.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_LARGE);
submitButton.setWidthFull(); 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 // Zurück-Link
Button backButton = new Button("Zurück zur Startseite", event -> Button backButton = new Button("Zurück zur Startseite", event ->
getUI().ifPresent(ui -> ui.navigate(""))); getUI().ifPresent(ui -> ui.navigate("")));
backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY); backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
backButton.setWidthFull(); 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; return container;
} }
private void registerUser() { private void onStartRegistration() {
var email = emailField.getValue().trim(); var email = emailField.getValue().trim();
var password = passwordField.getValue(); var password = passwordField.getValue();
var confirmPassword = confirmPasswordField.getValue(); var confirmPassword = confirmPasswordField.getValue();
@@ -106,50 +148,112 @@ public class RegisterView extends VerticalLayout {
emailField.focus(); emailField.focus();
return; return;
} }
if (!email.contains("@") || !email.contains(".")) { if (!email.contains("@") || !email.contains(".")) {
Notification.show("Bitte geben Sie eine gültige E-Mail-Adresse ein.", 3000, Notification.Position.MIDDLE); Notification.show("Bitte geben Sie eine gültige E-Mail-Adresse ein.", 3000, Notification.Position.MIDDLE);
emailField.focus(); emailField.focus();
return; return;
} }
if (userService.existsByEmail(email)) {
Notification.show("Ein Benutzer mit dieser E-Mail-Adresse existiert bereits.", 4000, Notification.Position.MIDDLE);
return;
}
if (password.isEmpty()) { if (password.isEmpty()) {
Notification.show("Bitte geben Sie ein Passwort ein.", 3000, Notification.Position.MIDDLE); Notification.show("Bitte geben Sie ein Passwort ein.", 3000, Notification.Position.MIDDLE);
passwordField.focus(); passwordField.focus();
return; return;
} }
if (password.length() < 6) { if (password.length() < 6) {
Notification.show("Das Passwort muss mindestens 6 Zeichen lang sein.", 3000, Notification.Position.MIDDLE); Notification.show("Das Passwort muss mindestens 6 Zeichen lang sein.", 3000, Notification.Position.MIDDLE);
passwordField.focus(); passwordField.focus();
return; return;
} }
if (!password.equals(confirmPassword)) { if (!password.equals(confirmPassword)) {
Notification.show("Die Passwörter stimmen nicht überein.", 3000, Notification.Position.MIDDLE); Notification.show("Die Passwörter stimmen nicht überein.", 3000, Notification.Position.MIDDLE);
confirmPasswordField.focus(); confirmPasswordField.focus();
return; 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 {
mailUtil.sendMail(email, subject, body);
awaitingVerification = true;
// UI umstellen: Code-Eingabe anzeigen
codeField.clear();
codeField.setVisible(true);
verifyButton.setVisible(true);
resendButton.setVisible(true);
emailField.setReadOnly(true);
passwordField.setReadOnly(true);
confirmPasswordField.setReadOnly(true);
submitButton.setEnabled(false);
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 { try {
// Benutzer erstellen
userService.createUser(email, password, "Benutzer", "Name"); userService.createUser(email, password, "Benutzer", "Name");
VaadinSession.getCurrent().setAttribute("flashMessage", "Registrierung erfolgreich. Bitte melden Sie sich an.");
// Erfolgsmeldung und Weiterleitung
Notification.show("Registrierung erfolgreich! Sie werden zur Anmeldung weitergeleitet.",
3000, Notification.Position.MIDDLE);
getUI().ifPresent(ui -> ui.navigate("login")); getUI().ifPresent(ui -> ui.navigate("login"));
} catch (RuntimeException e) { } catch (RuntimeException e) {
if (e.getMessage().contains("already exists")) { Notification.show("Registrierung fehlgeschlagen: " + e.getMessage(), 5000, Notification.Position.MIDDLE);
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);
} }
} }
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);
} }
} }