diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8b9ded --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# [User-Api-Jwt](https://github.com/Marc0Franc0/User-Api-Jwt#user-api-jwt) +Proyecto de muestra sobre cómo implementar la seguridad con autenticación JWT basada en Spring boot 3 y Spring security 6 + +## Características +- Registro de usuario e inicio de sesión con autenticación JWT +- Cifrado de contraseña usando BCrypt +- Autorización basada en roles con Spring Security + +## Tecnologías +- Spring Boot 3.0 +- Spring Security +- JSON Web Tokens (JWT) +- BCrypt +- Maven +- MySQL + +## Ejecución +1. Clonar repositorio: git clone https://github.com/Marc0Franc0/User-Api-Jwt.git +2. Ir al directorio del proyecto: cd User-Api-Jwt +3. Seguir pasos para ejecución con Maven + +## Requerimientos para ejecutar con Maven + +Para construir y ejecutar la aplicación necesita: + +- [JDK 17+](https://www.oracle.com/java/technologies/downloads/#java17) +- [Maven 3+](https://maven.apache.org) + +Configurar datos de la base de datos MySQL: [application.properties](https://github.com/Marc0Franc0/School-Management/blob/main/src/main/resources/application.properties) + +Configurar JWT: [application.properties](https://github.com/Marc0Franc0/School-Management/blob/main/src/main/resources/application.properties) +- jwt.secret.key = esYG4cI3/jsQ1f3b+25mesq9nXsN7kIU45hPr27FyRxMd7WteShs/5VnXv1YfgMR + (Se utiliza para firmar los tokens) +- jwt.time.expiration = 86400000 (equivalente a un día) + +Ejecutar localmente + +```shell +mvn clean install +``` +```shell +mvn spring-boot:run +``` + +Dirigirse a: [http://localhost:8080/](http://localhost:8080/) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3adb14e..deb06a4 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.app User-Api-Jwt - 1.0.0 + 1.1.0 User-Api-Jwt Proyecto de muestra sobre cómo implementar la seguridad con JWT basada en Spring boot 3 y Spring security 6 diff --git a/src/main/java/com/app/UserApiJwtApplication.java b/src/main/java/com/app/UserApiJwtApplication.java index 6cc8629..15fa4bf 100644 --- a/src/main/java/com/app/UserApiJwtApplication.java +++ b/src/main/java/com/app/UserApiJwtApplication.java @@ -24,7 +24,7 @@ public static void main(String[] args) { SpringApplication.run(UserApiJwtApplication.class, args); } - @Bean +/* @Bean CommandLineRunner init() { return args -> { @@ -38,5 +38,5 @@ CommandLineRunner init() { userRepository.save(userEntity); }; - } + }*/ } diff --git a/src/main/java/com/app/controller/AdminController.java b/src/main/java/com/app/controller/AdminController.java new file mode 100644 index 0000000..176fc6e --- /dev/null +++ b/src/main/java/com/app/controller/AdminController.java @@ -0,0 +1,14 @@ +package com.app.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("private") +public class AdminController { + @GetMapping("/") + public String hola(){ + return "Hola admin"; + } +} diff --git a/src/main/java/com/app/controller/AuthController.java b/src/main/java/com/app/controller/AuthController.java new file mode 100644 index 0000000..0dfc3f3 --- /dev/null +++ b/src/main/java/com/app/controller/AuthController.java @@ -0,0 +1,23 @@ +package com.app.controller; + +import com.app.dto.RegisterDTO; +import com.app.service.RegisterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("auth") +public class AuthController { + @Autowired + RegisterService registerService; + //Endpoint para poder registrarse y tener acceso + @PostMapping("/register") + public ResponseEntity register(@RequestBody RegisterDTO registerDTO){ + return ResponseEntity.status(HttpStatus.OK).body(registerService.registerUser(registerDTO)); + } +} diff --git a/src/main/java/com/app/controller/Controller.java b/src/main/java/com/app/controller/Controller.java index f297712..ab301c1 100644 --- a/src/main/java/com/app/controller/Controller.java +++ b/src/main/java/com/app/controller/Controller.java @@ -1,9 +1,11 @@ package com.app.controller; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController +@RequestMapping("public") public class Controller { @GetMapping("/") public String hola(){ diff --git a/src/main/java/com/app/dto/RegisterDTO.java b/src/main/java/com/app/dto/RegisterDTO.java new file mode 100644 index 0000000..e6f8da2 --- /dev/null +++ b/src/main/java/com/app/dto/RegisterDTO.java @@ -0,0 +1,13 @@ +package com.app.dto; + +import lombok.Builder; +import lombok.Getter; +import java.util.Set; + +@Builder +@Getter +public class RegisterDTO { + private String username; + private String password; + private Setroles; +} diff --git a/src/main/java/com/app/model/UserEntity.java b/src/main/java/com/app/model/UserEntity.java index 7e5e077..edf5110 100644 --- a/src/main/java/com/app/model/UserEntity.java +++ b/src/main/java/com/app/model/UserEntity.java @@ -13,7 +13,7 @@ @AllArgsConstructor @NoArgsConstructor @Entity -public class UserEntity extends Person{ +public class UserEntity{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; diff --git a/src/main/java/com/app/config/SecurityConfig.java b/src/main/java/com/app/security/SecurityConfig.java similarity index 81% rename from src/main/java/com/app/config/SecurityConfig.java rename to src/main/java/com/app/security/SecurityConfig.java index 291c679..b0dbbf3 100644 --- a/src/main/java/com/app/config/SecurityConfig.java +++ b/src/main/java/com/app/security/SecurityConfig.java @@ -1,24 +1,19 @@ -package com.app.config; +package com.app.security; -import com.app.jwt.JwtAuthenticationFilter; -import com.app.jwt.JwtAuthorizationFilter; -import com.app.jwt.JwtTokenProvider; +import com.app.security.jwt.JwtAuthenticationFilter; +import com.app.security.jwt.JwtAuthorizationFilter; +import com.app.security.jwt.JwtTokenProvider; import com.app.service.UserDetailsServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @@ -74,7 +69,14 @@ SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager .authorizeHttpRequests( //Configuración de acceso a los endpoints auth-> { - auth.anyRequest().permitAll(); + //endpoint para registrarse permitido para todos + auth.requestMatchers("/auth/register").permitAll(); + //endpoint público con acceso para usuarios y administradores + auth.requestMatchers("/public/**").hasAnyRole("USER","ADMIN"); + //endpoint privado con acceso solo para administradores + auth.requestMatchers("/private/**").hasRole("ADMIN"); + //para demas endpint solo se debe estar autenticado + auth.anyRequest().authenticated(); } ) //Filtro creado el cual es necesario para autenticar un usuario con su username y password diff --git a/src/main/java/com/app/jwt/JwtAuthenticationFilter.java b/src/main/java/com/app/security/jwt/JwtAuthenticationFilter.java similarity index 99% rename from src/main/java/com/app/jwt/JwtAuthenticationFilter.java rename to src/main/java/com/app/security/jwt/JwtAuthenticationFilter.java index 5669c4a..44f2c7d 100644 --- a/src/main/java/com/app/jwt/JwtAuthenticationFilter.java +++ b/src/main/java/com/app/security/jwt/JwtAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.app.jwt; +package com.app.security.jwt; import com.app.model.UserEntity; import com.fasterxml.jackson.core.exc.StreamReadException; diff --git a/src/main/java/com/app/jwt/JwtAuthorizationFilter.java b/src/main/java/com/app/security/jwt/JwtAuthorizationFilter.java similarity index 99% rename from src/main/java/com/app/jwt/JwtAuthorizationFilter.java rename to src/main/java/com/app/security/jwt/JwtAuthorizationFilter.java index 4cb1835..6f37014 100644 --- a/src/main/java/com/app/jwt/JwtAuthorizationFilter.java +++ b/src/main/java/com/app/security/jwt/JwtAuthorizationFilter.java @@ -1,4 +1,4 @@ -package com.app.jwt; +package com.app.security.jwt; import java.io.IOException; diff --git a/src/main/java/com/app/jwt/JwtTokenProvider.java b/src/main/java/com/app/security/jwt/JwtTokenProvider.java similarity index 99% rename from src/main/java/com/app/jwt/JwtTokenProvider.java rename to src/main/java/com/app/security/jwt/JwtTokenProvider.java index 5187a10..45b592e 100644 --- a/src/main/java/com/app/jwt/JwtTokenProvider.java +++ b/src/main/java/com/app/security/jwt/JwtTokenProvider.java @@ -1,4 +1,4 @@ -package com.app.jwt; +package com.app.security.jwt; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; diff --git a/src/main/java/com/app/service/RegisterService.java b/src/main/java/com/app/service/RegisterService.java new file mode 100644 index 0000000..f367e8e --- /dev/null +++ b/src/main/java/com/app/service/RegisterService.java @@ -0,0 +1,43 @@ +package com.app.service; + +import com.app.dto.RegisterDTO; +import com.app.model.ERole; +import com.app.model.RoleEntity; +import com.app.model.UserEntity; +import com.app.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.Set; +import java.util.stream.Collectors; + +@Service +public class RegisterService { + @Autowired + UserRepository userRepository; + @Autowired + PasswordEncoder passwordEncoder; + public UserEntity registerUser(RegisterDTO registerDTO){ + + Set roles = + //obtención de roles + registerDTO.getRoles() + //mapeamiento de cada uno + .stream().map(role -> RoleEntity.builder() + .name(ERole.valueOf(role)) + .build()) + .collect(Collectors.toSet()); + + UserEntity userEntity = UserEntity.builder() + //nombre de usuario + .username(registerDTO.getUsername()) + //password encriptada + .password(passwordEncoder.encode(registerDTO.getPassword())) + //roles del usuario + .roles(roles) + .build(); + + return userRepository.save(userEntity); + } +}