lista de usuarios terminada

This commit is contained in:
2025-09-26 17:57:06 +02:00
parent 01a1ac4b71
commit 88b43847f0
29 changed files with 27681 additions and 138 deletions

View File

@ -6,6 +6,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ -22,84 +24,107 @@ import com.imprimelibros.erp.users.UserDetailsImpl;
@Configuration
public class SecurityConfig {
private final DataSource dataSource;
private final DataSource dataSource;
public SecurityConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
public SecurityConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
// UserDetailsService para autenticación por username
@Bean
public UserDetailsService userDetailsService(UserDao repo) {
return username -> {
User u = repo.findByUserNameAndEnabledTrue(username);
if (u == null)
throw new UsernameNotFoundException("No existe: " + username);
return new UserDetailsImpl(u);
};
}
// ========== Beans base ==========
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService(UserDao repo) {
return username -> {
User u = repo.findByUserNameAndEnabledTrue(username);
if (u == null) throw new UsernameNotFoundException("No existe: " + username);
return new UserDetailsImpl(u);
};
}
// Repositorio de tokens persistentes (usa la tabla 'persistent_logins')
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
// Descomenta una única vez si quieres que cree la tabla automáticamente:
// repo.setCreateTableOnStartup(true);
return repo;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, @Value("${security.rememberme.key}") String keyRememberMe) throws Exception {
http
.sessionManagement(session -> session
.invalidSessionUrl("/login?expired")
.maximumSessions(1) // opcional: limita sesiones concurrentes
)
// CSRF habilitado; ignoramos endpoints públicos del presupuesto (AJAX)
.csrf(csrf -> csrf.ignoringRequestMatchers("/presupuesto/public/**"))
// Provider que soporta UsernamePasswordAuthenticationToken
@Bean
public AuthenticationProvider daoAuthenticationProvider(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/", "/login",
"/assets/**", "/css/**", "/js/**", "/images/**",
"/public/**", "/presupuesto/public/**",
"/error", "/favicon.ico")
.permitAll()
.anyRequest().authenticated())
DaoAuthenticationProvider p = new DaoAuthenticationProvider();
p.setUserDetailsService(userDetailsService);
p.setPasswordEncoder(passwordEncoder);
return p;
}
.authorizeHttpRequests(configurer -> configurer
.requestMatchers("/users/**").hasAnyRole("SUPERADMIN", "ADMIN")
)
.formLogin(login -> login
.loginPage("/login").permitAll()
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/", true))
// Remember-me (tabla persistent_logins)
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
// repo.setCreateTableOnStartup(true); // solo 1ª vez si necesitas crear la tabla
return repo;
}
// ===== Remember Me =====
.rememberMe(rm -> rm
.key(keyRememberMe) // clave secreta
.rememberMeParameter("remember-me") // <input name="remember-me">
.rememberMeCookieName("IMPRIMELIBROS_REMEMBER")
.tokenValiditySeconds(60 * 60 * 24 * 14) // 14 días
.userDetailsService(userDetailsService(null)) // se inyecta el bean real
// en runtime
.tokenRepository(persistentTokenRepository()))
// ========== Filtro de seguridad ==========
@Bean
public SecurityFilterChain securityFilterChain(
HttpSecurity http,
@Value("${security.rememberme.key}") String keyRememberMe,
UserDetailsService userDetailsService,
PersistentTokenRepository tokenRepo,
AuthenticationProvider daoAuthenticationProvider
) throws Exception {
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "IMPRIMELIBROS_REMEMBER")
.permitAll());
http
// Registra explícitamente el provider para Username/Password
.authenticationProvider(daoAuthenticationProvider)
return http.build();
}
.sessionManagement(session -> session
.invalidSessionUrl("/login?expired")
.maximumSessions(1)
)
.csrf(csrf -> csrf
.ignoringRequestMatchers("/presupuesto/public/**")
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/login",
"/assets/**", "/css/**", "/js/**", "/images/**",
"/public/**", "/presupuesto/public/**",
"/error", "/favicon.ico").permitAll()
.requestMatchers("/users/**").hasAnyRole("SUPERADMIN", "ADMIN")
.anyRequest().authenticated()
)
.formLogin(login -> login
.loginPage("/login").permitAll()
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/", true)
.failureUrl("/login?error") // útil para diagnosticar
)
.rememberMe(rm -> rm
.key(keyRememberMe)
.rememberMeParameter("remember-me")
.rememberMeCookieName("IMPRIMELIBROS_REMEMBER")
.tokenValiditySeconds(60 * 60 * 24 * 2)
.userDetailsService(userDetailsService)
.tokenRepository(tokenRepo)
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "IMPRIMELIBROS_REMEMBER")
.permitAll()
);
return http.build();
}
}