feat(auth): add password reset functionality and DTO

This commit is contained in:
csimonis 2025-05-15 12:15:33 +02:00 committed by Phan Huy Tran
commit c8f2d16f07
8 changed files with 55 additions and 11 deletions

View file

@ -4,6 +4,7 @@ import de.szut.casino.exceptionHandling.ErrorDetails;
import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException; import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.security.dto.AuthResponseDto; import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.dto.LoginRequestDto; import de.szut.casino.security.dto.LoginRequestDto;
import de.szut.casino.security.dto.ResetPasswordDto;
import de.szut.casino.security.service.AuthService; import de.szut.casino.security.service.AuthService;
import de.szut.casino.user.dto.CreateUserDto; import de.szut.casino.user.dto.CreateUserDto;
import de.szut.casino.user.dto.GetUserDto; import de.szut.casino.user.dto.GetUserDto;
@ -49,4 +50,10 @@ public class AuthController {
authService.recoverPassword(email); authService.recoverPassword(email);
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
@PostMapping("/reset-password")
public ResponseEntity<Void> resetPassword(@Valid @RequestBody ResetPasswordDto passwordDto) throws MessagingException, IOException {
authService.resetPassword(passwordDto);
return ResponseEntity.ok().build();
}
} }

View file

@ -0,0 +1,15 @@
package de.szut.casino.security.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class ResetPasswordDto {
private String token;
private String password;
}

View file

@ -3,17 +3,20 @@ package de.szut.casino.security.service;
import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException; import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.security.dto.AuthResponseDto; import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.dto.LoginRequestDto; import de.szut.casino.security.dto.LoginRequestDto;
import de.szut.casino.security.dto.ResetPasswordDto;
import de.szut.casino.security.jwt.JwtUtils; import de.szut.casino.security.jwt.JwtUtils;
import de.szut.casino.user.UserEntity; import de.szut.casino.user.UserEntity;
import de.szut.casino.user.UserService; import de.szut.casino.user.UserService;
import de.szut.casino.user.dto.CreateUserDto; import de.szut.casino.user.dto.CreateUserDto;
import de.szut.casino.user.dto.GetUserDto; import de.szut.casino.user.dto.GetUserDto;
import jakarta.mail.MessagingException; import jakarta.mail.MessagingException;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.swing.text.html.Option; import javax.swing.text.html.Option;
@ -35,6 +38,9 @@ public class AuthService {
@Autowired @Autowired
private EmailService emailService; private EmailService emailService;
@Autowired
private PasswordEncoder passwordEncoder;
public AuthResponseDto login(LoginRequestDto loginRequest) throws EmailNotVerifiedException { public AuthResponseDto login(LoginRequestDto loginRequest) throws EmailNotVerifiedException {
if (!userService.isVerified(loginRequest.getUsernameOrEmail())) { if (!userService.isVerified(loginRequest.getUsernameOrEmail())) {
throw new EmailNotVerifiedException(); throw new EmailNotVerifiedException();
@ -81,15 +87,25 @@ public class AuthService {
return true; return true;
} }
public void recoverPassword(String email) { public void recoverPassword(String email) throws MessagingException, IOException {
Optional<UserEntity> optionalUser = userService.getUserByEmail(email); Optional<UserEntity> optionalUser = userService.getUserByEmail(email);
if (optionalUser.isPresent()) { if (optionalUser.isPresent()) {
UserEntity user = optionalUser.get(); UserEntity user = optionalUser.get();
String token = jwtUtils.generateToken(user.getUsername()); user.setPasswordResetToken(RandomStringUtils.randomAlphanumeric(64));
user.setVerificationToken(token);
userService.saveUser(user); userService.saveUser(user);
this.emailService.sendPasswordRecoveryEmail(user); this.emailService.sendPasswordRecoveryEmail(user);
} }
} }
public void resetPassword(ResetPasswordDto passwordDto) {
Optional<UserEntity> optionalUser = userService.getUserByPasswordResetToken(passwordDto.getToken());
if (optionalUser.isPresent()) {
UserEntity user = optionalUser.get();
user.setPassword(passwordEncoder.encode(passwordDto.getPassword()));
user.setPasswordResetToken(null);
userService.saveUser(user);
}
}
} }

View file

@ -91,6 +91,7 @@ public class EmailService {
String template = loadTemplate("email/recover-password.html"); String template = loadTemplate("email/recover-password.html");
String htmlContent = template String htmlContent = template
.replace("${username}", user.getUsername()) .replace("${username}", user.getUsername())
.replace("${resetToken}", user.getPasswordResetToken())
.replace("${feUrl}", feUrl); .replace("${feUrl}", feUrl);
MimeMessage message = mailSender.createMimeMessage(); MimeMessage message = mailSender.createMimeMessage();

View file

@ -34,6 +34,8 @@ public class UserEntity {
private String verificationToken; private String verificationToken;
private String passwordResetToken;
public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) { public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) {
this.email = email; this.email = email;
this.username = username; this.username = username;

View file

@ -21,4 +21,7 @@ public interface UserRepository extends JpaRepository<UserEntity, Long> {
@Query("SELECT u FROM UserEntity u WHERE u.username = ?1 OR u.email = ?1") @Query("SELECT u FROM UserEntity u WHERE u.username = ?1 OR u.email = ?1")
Optional<UserEntity> findOneByUsernameOrEmail(String usernameOrEmail); Optional<UserEntity> findOneByUsernameOrEmail(String usernameOrEmail);
@Query("SELECT u FROM UserEntity u WHERE u.passwordResetToken = ?1")
Optional<UserEntity> findOneByPasswordResetToken(String token);
} }

View file

@ -66,4 +66,8 @@ public class UserService {
public Optional<UserEntity> getUserByEmail(String email) { public Optional<UserEntity> getUserByEmail(String email) {
return userRepository.findByEmail(email); return userRepository.findByEmail(email);
} }
public Optional<UserEntity> getUserByPasswordResetToken(String token) {
return this.userRepository.findOneByPasswordResetToken(token);
}
} }

View file

@ -129,12 +129,8 @@
<a href="${feUrl}/reset-password?token=${resetToken}" class="button">Passwort zurücksetzen</a> <a href="${feUrl}/reset-password?token=${resetToken}" class="button">Passwort zurücksetzen</a>
</div> </div>
<p>Alternativ können Sie auch diesen Code verwenden:</p>
<div class="recovery-code">${resetCode}</div>
<div class="info-box"> <div class="info-box">
<p><span class="warning">Hinweis:</span> Dieser Link und Code sind aus Sicherheitsgründen nur <span class="highlight">60 Minuten</span> gültig.</p> <p><span class="warning">Hinweis:</span> Dieser Link und Code sind aus Sicherheitsgründen vielleicht nur <span class="highlight">60 Minuten</span> gültig.</p>
</div> </div>
<div class="divider"></div> <div class="divider"></div>
@ -145,15 +141,15 @@
<div class="divider"></div> <div class="divider"></div>
<p>Bei Fragen steht Ihnen unser Support-Team jederzeit zur Verfügung.</p> <p>Bei Fragen steht Ihnen unser Support-Team nicht zur Verfügung.</p>
<p>Mit freundlichen Grüßen,<br> <p>Mit freundlichen Grüßen,<br>
Ihr <span style="color: #10b981;">Trustworthy Casino</span> Team</p> Ihr <span style="color: #10b981;">Trustworthy Casino</span> Team</p>
</div> </div>
<div class="footer"> <div class="footer">
<p>2025 Trustworthy Casino - Alle Rechte vorbehalten</p> <p>2025 Trustworthy Casino - Keine Rechte vorbehalten</p>
<p>Diese E-Mail wurde automatisch generiert. Bitte antworten Sie nicht darauf.</p> <p>Diese E-Mail wurde automatisch generiert. Bitte antworten Sie nicht darauf.</p>
<p>Für Fragen zur Sicherheit Ihres Kontos kontaktieren Sie uns bitte unter <span style="color: #10b981;">security@trustworthycasino.com</span></p> <p>Für Fragen zur Sicherheit Ihres Kontos kontaktieren Sie uns bitte nicht.</p>
</div> </div>
</div> </div>
</body> </body>