Simple sistema de autenticación seguro usando JSON Web Tokens (JWT) con roles que permite un CRUD usuarios Aquí tienes el README completo para tu proyecto de autenticación con Spring Boot y JWT, siguiendo los principios SOLID:
src/main/java/
└── com/velasquez/authentication/demo/
├── config/
│ ├── ApplicationConfig.java
│ └── SecurityConfig.java
├── controller/
│ ├── AuthenticationController.java
│ └── UserController.java
├── dto/
│ ├── AuthenticationRequest.java
│ ├── AuthenticationResponse.java
│ └── RegisterRequest.java
├── entity/
│ ├── Role.java
│ └── Users.java
├── repository/
│ └── UserRepository.java
├── security/
│ ├── JwtAuthenticationFilter.java
│ └── JwtService.java
├── service/
│ ├── AuthenticationService.java
│ └── UserServices.java
└── DemoApplication.java
Leyenda:
- 📁
config/: Configuraciones de la aplicación - 📁
controller/: Controladores REST - 📁
dto/: Objetos de Transferencia de Datos - 📁
entity/: Entidades del dominio - 📁
repository/: Interfaces de acceso a datos - 📁
security/: Componentes de seguridad - 📁
service/: Lógica de negocio - 🚀
DemoApplication.java: Clase principal
| Grupo ID | Artifact ID | Versión | Descripción |
|---|---|---|---|
org.springframework.boot |
spring-boot-starter-data-jpa |
- | Integración con JPA/Hibernate para acceso a datos |
org.springframework.boot |
spring-boot-starter-security |
- | Spring Security para autenticación y autorización |
org.springframework.boot |
spring-boot-starter-web |
- | Construcción de aplicaciones web REST |
| Grupo ID | Artifact ID | Versión | Uso |
|---|---|---|---|
io.jsonwebtoken |
jjwt-api |
0.11.5 | API para creación/validación de JWT |
io.jsonwebtoken |
jjwt-impl |
0.11.5 | Implementación runtime de JJWT |
io.jsonwebtoken |
jjwt-jackson |
0.11.5 | Soporte para serialización Jackson |
<!-- Base de datos MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok para reducción de código boilerplate -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency><plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<!-- Configuración especial para Lombok -->
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>spring:
datasource:
url: jdbc:mysql://localhost:3306/jwt_auth
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.DriverVariables:
url: Conexión a la base de datos MySQLusername/password: Credenciales de accesodriver-class-name: Controlador JDBC para MySQL
spring:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQL8DialectOpciones clave:
ddl-auto: create-drop→ Crea y elimina tablas al iniciar/detener (solo desarrollo)show-sql: true→ Muestra SQL en consolaformat_sql: true→ Formatea el SQL para mejor legibilidaddialect→ Optimizado para MySQL 8+
jwt:
secret-key: c2d112bff0bb34f4be5d9b8553270e26f9c9e09a18c40e885a5d961259e45d53
expiration: 86400000 # 24 horasSeguridad:
secret-key: Clave HMAC-SHA256 para firmar tokensexpiration: Tiempo de vida del token (ms)
| Método | Endpoint | Descripción | Acceso |
|---|---|---|---|
| POST | /api/v1/auth/register | Registrar nuevo usuario | Público |
| POST | /api/v1/auth/login | Autenticación (obtener JWT) | Público |
| GET | /api/v1/users | Listar todos usuarios | ROLE_ADMIN |
| GET | /api/v1/users/{id} | Obtener usuario por ID | ROLE_USER/ADMIN |
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class Users implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String password;
@Enumerated(EnumType.STRING)
private Role role;
// Implementación de UserDetails...
}Función:
- Representa la tabla de usuarios en la base de datos
- Implementa
UserDetailspara integración con Spring Security - Usa anotaciones JPA para mapeo objeto-relacional
- Incluye validación de campos obligatorios
public enum Role {
ROLE_USER, ROLE_ADMIN
}Función:
- Define los roles disponibles en el sistema
- Usa el prefijo
ROLE_requerido por Spring Security
@Repository
public interface UserRepository extends JpaRepository<Users, Integer> {
Optional<Users> findByEmail(String email);
}Función:
- Proporciona operaciones CRUD automáticas
- Incluye método personalizado para buscar por email
- Sigue el principio de Segregación de Interfaces (SOLID) al tener solo métodos necesarios
@Service
@RequiredArgsConstructor
public class UserServices {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public List<Users> getAllUsers() { ... }
public Users getUserById(Integer id) { ... }
public Users createUser(Users user) { ... }
public Users updateUser(Integer id, Users userDetails) { ... }
public void deleteUser(Integer id) { ... }
}Responsabilidades:
- Lógica de negocio para gestión de usuarios
- Encriptación de contraseñas
- Validación de reglas de negocio
- Sigue el Principio de Responsabilidad Única (SOLID)
@Service
@RequiredArgsConstructor
public class AuthenticationService {
private final UserRepository repository;
private final PasswordEncoder passwordEncoder;
private final JwtService jwtService;
private final AuthenticationManager authenticationManager;
public AuthenticationResponse register(RegisterRequest request) { ... }
public AuthenticationResponse authenticate(AuthenticationRequest request) { ... }
}Responsabilidades:
- Registro de nuevos usuarios
- Autenticación y generación de JWT
- Delegación de responsabilidades a otros componentes
@RestController
@RequestMapping("/api/v1/auth")
public class AuthenticationController {
private final AuthenticationService authenticationService;
@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> register(...) { ... }
@PostMapping("/authenticate")
public ResponseEntity<AuthenticationResponse> authenticate(...) { ... }
}Endpoints:
POST /api/v1/auth/register: Registro de usuariosPOST /api/v1/auth/authenticate: Autenticación (login)
/**
* Controlador para gestión de usuarios.
* Sigue el principio de Segregación de Interfaces (SOLID)
*/
@RestController
@RequestMapping("/api/v1/users")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public class UserController {
private final UserServices userService;
@GetMapping
public List<Users> getAllUsers() { ... }
@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
public Users getUserById(...) { ... }
// Otros endpoints CRUD...
}Seguridad:
- Control de acceso basado en roles
- Anotaciones
@PreAuthorizepara autorización granular
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
private final JwtAuthenticacionFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}Configuraciones clave:
- Deshabilita CSRF (común en APIs stateless)
- Permite acceso público a endpoints de autenticación
- Configura política de sesiones STATELESS
- Registra el filtro JWT
@Configuration
public class ApplicationConfig {
@Bean
public UserDetailsService userDetailsService() {
return username -> repository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}Componentes creados:
- UserDetailsService personalizado
- Proveedor de autenticación
- Codificador de contraseñas BCrypt
@Component
public class JwtAuthenticacionFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(...) {
// 1. Extraer token del header
// 2. Validar token
// 3. Cargar UserDetails
// 4. Establecer autenticación en SecurityContext
}
}Flujo:
- Intercepta cada solicitud
- Extrae y valida JWT
- Establece autenticación en contexto de seguridad
@Service
public class JwtService {
@Value("${jwt.secret-key}")
private String SECRET_KEY;
@Value("${jwt.expiration}")
private long jwtExpiration;
public String extractUserName(String token){...}
public <T> T extractClaim){...}
public List<String> extractRoles){...}
public String generateToken(UserDetails userDetails){...}
public String generateToken){...}
public boolean isTokenValid(String token){...}
private boolean isTokenExpired(String token){...}
private Date extractExpiration(String token){...}
private Claims extractAllClaims(String token){...}
private Key getSignInKey(){...}
}Funcionalidades:
- Generación de tokens JWT
- Validación de tokens
- Extracción de claims
- Configurable mediante properties
| Concepto | Explicación | Relación con SOLID |
|---|---|---|
| Inversión de Dependencias | Los componentes dependen de abstracciones | Principio D (DIP) |
| Single Responsibility | Cada clase tiene una única responsabilidad | Principio S (SRP) |
| JWT Authentication | Token-based stateless authentication | - |
| Spring Security | Framework para autenticación/autorización | - |
| BCryptPasswordEncoder | Encriptación segura de contraseñas | - |
| Repository Pattern | Abstracción para acceso a datos | Principio I (ISP) |
-
Requisitos:
- Java 17+
- Maven
- Base de datos configurada (añadir config en
application.properties)
-
Endpoints principales:
POST /api/v1/auth/register- Registrar nuevo usuarioPOST /api/v1/auth/authenticate- Obtener token JWTGET /api/v1/users- Listar usuarios (requiere rol ADMIN)
-
Ejemplo de registro:
POST /api/v1/auth/register
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"password": "password123",
"role": "ROLE_ADMIN"
}Observaciones:
- Al enviar el JSON, escribir correctamente los atributos
- Si no se envia el role, se registra como ROLE_USER
- Ejemplo de autenticación:
POST /api/v1/auth/authenticate
{
"email": "john@example.com",
"password": "password123"
}