trabajando en el registro de usuarios

This commit is contained in:
2025-10-03 13:57:22 +02:00
parent 1e24065fb7
commit 5da73a3679
12 changed files with 296 additions and 2 deletions

View File

@ -124,6 +124,7 @@ public class SecurityConfig {
.requestMatchers(
"/",
"/login",
"/signup",
"/assets/**",
"/css/**",
"/js/**",

View File

@ -15,5 +15,11 @@ public class LoginController {
return "imprimelibros/login/login";
}
@GetMapping("/signup")
public String signup(Model model, Locale locale) {
model.addAttribute("form", "_signup");
return "imprimelibros/login/login";
}
}

View File

@ -0,0 +1,97 @@
package com.imprimelibros.erp.login;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.imprimelibros.erp.common.email.EmailService;
import com.imprimelibros.erp.login.dto.SignupForm;
import com.imprimelibros.erp.users.User;
import com.imprimelibros.erp.users.UserDao;
import org.springframework.security.crypto.password.PasswordEncoder;
@Service
public class SignupService {
private final UserDao userRepository;
private final PasswordEncoder passwordEncoder;
private final VerificationTokenRepository tokenRepository;
private final EmailService emailService;
// minutos de validez del token
private static final long TOKEN_MINUTES = 60;
public SignupService(UserDao userRepository,
PasswordEncoder passwordEncoder,
VerificationTokenRepository tokenRepository,
EmailService emailService) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.tokenRepository = tokenRepository;
this.emailService = emailService;
}
@Transactional
public void register(SignupForm form) {
if (!form.getPassword().equals(form.getPasswordConfirm())) {
throw new IllegalArgumentException("Las contraseñas no coinciden");
}
if (userRepository.existsByUsername(form.getUsername())) {
throw new IllegalArgumentException("El correo ya está registrado");
}
// Crear usuario deshabilitado
User user = new User();
user.setUsername(form.getUsername().trim().toLowerCase());
user.setPassword(passwordEncoder.encode(form.getPassword()));
user.setEnabled(false);
// TODO: asignar rol por defecto si aplica (e.g., ROLE_USER)
user = userRepository.save(user);
// Generar token
var token = VerificationToken.create(user.getId(), TOKEN_MINUTES);
tokenRepository.save(token);
// Construir URL absoluta para /verify
String verifyUrl = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/verify")
.queryParam("token", token.getToken())
.build()
.toUriString();
// Enviar correo
Map<String, Object> model = new HashMap<>();
model.put("verifyUrl", verifyUrl);
model.put("minutes", TOKEN_MINUTES);
emailService.sendTemplate(
user.getUsername(),
"Confirma tu correo | ImprimeLibros ERP",
"mail/verify-email",
model);
}
@Transactional
public boolean verify(String tokenValue) {
var tokenOpt = tokenRepository.findByToken(tokenValue);
if (tokenOpt.isEmpty()) return false;
var token = tokenOpt.get();
if (token.isUsed() || token.isExpired()) return false;
var user = userRepository.findById(token.getUserId())
.orElseThrow(() -> new IllegalStateException("Usuario no encontrado para el token"));
user.setEnabled(true);
userRepository.save(user);
token.setUsedAt(java.time.LocalDateTime.now());
tokenRepository.save(token);
return true;
}
}

View File

@ -0,0 +1,54 @@
package com.imprimelibros.erp.login;
import java.time.LocalDateTime;
import java.util.UUID;
import jakarta.persistence.*;
@Entity
@Table(name = "verification_tokens", indexes = {
@Index(name = "idx_verification_token_token", columnList = "token", unique = true)
})
public class VerificationToken {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false, unique=true, length=64)
private String token;
@Column(nullable=false)
private Long userId;
@Column(nullable=false)
private LocalDateTime createdAt;
@Column(nullable=false)
private LocalDateTime expiresAt;
private LocalDateTime usedAt;
public static VerificationToken create(Long userId, long minutesValid) {
VerificationToken t = new VerificationToken();
t.token = UUID.randomUUID().toString().replace("-", "");
t.userId = userId;
t.createdAt = LocalDateTime.now();
t.expiresAt = t.createdAt.plusMinutes(minutesValid);
return t;
}
// getters/setters
public Long getId() { return id; }
public String getToken() { return token; }
public void setToken(String token) { this.token = token; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getExpiresAt() { return expiresAt; }
public void setExpiresAt(LocalDateTime expiresAt) { this.expiresAt = expiresAt; }
public LocalDateTime getUsedAt() { return usedAt; }
public void setUsedAt(LocalDateTime usedAt) { this.usedAt = usedAt; }
public boolean isUsed() { return usedAt != null; }
public boolean isExpired() { return LocalDateTime.now().isAfter(expiresAt); }
}

View File

@ -0,0 +1,9 @@
package com.imprimelibros.erp.login;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface VerificationTokenRepository extends JpaRepository<VerificationToken, Long> {
Optional<VerificationToken> findByToken(String token);
}

View File

@ -0,0 +1,25 @@
package com.imprimelibros.erp.login.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class SignupForm {
@NotBlank @Email
private String username;
@NotBlank @Size(min = 8, message = "La contraseña debe tener al menos 8 caracteres")
private String password;
@NotBlank
private String passwordConfirm;
// getters/setters
public String getUsername() { return username; }
public void setUsername(String u) { this.username = u; }
public String getPassword() { return password; }
public void setPassword(String p) { this.password = p; }
public String getPasswordConfirm() { return passwordConfirm; }
public void setPasswordConfirm(String pc) { this.passwordConfirm = pc; }
}