From 4712d6904d7d8222b2b7e81b165f60dfbad35f51 Mon Sep 17 00:00:00 2001 From: Marc0Franc0 Date: Tue, 8 Aug 2023 15:58:47 -0300 Subject: [PATCH] Update v1.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -Creación de usuario Admin al iniciar api -Cambios en los endpoints para registrarse y logearse -Implementación de métodos de UserDetails en UserEntity -Modificación en la creación de Objeto User en UserDetailsServiceImpl --- pom.xml | 2 +- .../java/com/app/UserApiJwtApplication.java | 14 +++++-- .../com/app/controller/AdminController.java | 24 +++++++++--- .../com/app/controller/AuthController.java | 23 ----------- .../java/com/app/controller/Controller.java | 23 ++++++++--- src/main/java/com/app/dto/RegisterDTO.java | 3 ++ src/main/java/com/app/model/UserEntity.java | 38 ++++++++++++++++++- .../java/com/app/security/SecurityConfig.java | 8 ++-- .../app/service/UserDetailsServiceImpl.java | 29 ++------------ .../com/app/service/UserEntityService.java | 10 +++++ ...ervice.java => UserEntityServiceImpl.java} | 19 ++++++++-- src/main/resources/application.yml | 4 ++ 12 files changed, 123 insertions(+), 74 deletions(-) delete mode 100644 src/main/java/com/app/controller/AuthController.java create mode 100644 src/main/java/com/app/service/UserEntityService.java rename src/main/java/com/app/service/{RegisterService.java => UserEntityServiceImpl.java} (71%) diff --git a/pom.xml b/pom.xml index deb06a4..4ecf269 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.app User-Api-Jwt - 1.1.0 + 1.1.1 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 15fa4bf..f038aa7 100644 --- a/src/main/java/com/app/UserApiJwtApplication.java +++ b/src/main/java/com/app/UserApiJwtApplication.java @@ -5,6 +5,7 @@ import com.app.model.UserEntity; import com.app.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -20,17 +21,22 @@ public class UserApiJwtApplication { @Autowired UserRepository userRepository; + @Value("${user.admin.username}") + String usernameAdmin; + @Value("${user.admin.password}") + String passwordAdmin; + public static void main(String[] args) { SpringApplication.run(UserApiJwtApplication.class, args); } -/* @Bean +@Bean CommandLineRunner init() { return args -> { UserEntity userEntity = UserEntity.builder() - .username("marco") - .password(passwordEncoder.encode("1234")) + .username(usernameAdmin) + .password(passwordEncoder.encode(passwordAdmin)) .roles(Set.of(RoleEntity.builder() .name(ERole.valueOf(ERole.ADMIN.name())) .build())) @@ -38,5 +44,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 index 176fc6e..6234c0e 100644 --- a/src/main/java/com/app/controller/AdminController.java +++ b/src/main/java/com/app/controller/AdminController.java @@ -1,14 +1,26 @@ 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; +import com.app.dto.RegisterDTO; +import com.app.service.UserEntityService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Set; @RestController @RequestMapping("private") public class AdminController { - @GetMapping("/") - public String hola(){ - return "Hola admin"; + @Autowired + UserEntityService userEntityService; + + //Endpoint para poder registrarse y tener acceso como admin + @PostMapping("/auth/register") + public ResponseEntity register(@RequestBody RegisterDTO registerDTO) { + RegisterDTO user = registerDTO; + user.getRoles().clear(); + user.setRoles(Set.of("ADMIN","USER")); + return ResponseEntity.status(HttpStatus.OK).body(userEntityService.registerUser(registerDTO)); } } diff --git a/src/main/java/com/app/controller/AuthController.java b/src/main/java/com/app/controller/AuthController.java deleted file mode 100644 index 0dfc3f3..0000000 --- a/src/main/java/com/app/controller/AuthController.java +++ /dev/null @@ -1,23 +0,0 @@ -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 ab301c1..8cda2cc 100644 --- a/src/main/java/com/app/controller/Controller.java +++ b/src/main/java/com/app/controller/Controller.java @@ -1,14 +1,25 @@ 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; +import com.app.dto.RegisterDTO; +import com.app.service.UserEntityService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Set; @RestController @RequestMapping("public") public class Controller { - @GetMapping("/") - public String hola(){ - return "Hola"; + @Autowired + UserEntityService userEntityService; + //Endpoint para poder registrarse y tener acceso + @PostMapping("/auth/register") + public ResponseEntity register(@RequestBody RegisterDTO registerDTO){ + RegisterDTO user = registerDTO; + user.getRoles().clear(); + user.setRoles(Set.of("USER")); + return ResponseEntity.status(HttpStatus.OK).body(userEntityService.registerUser(registerDTO)); } } diff --git a/src/main/java/com/app/dto/RegisterDTO.java b/src/main/java/com/app/dto/RegisterDTO.java index e6f8da2..2f93ccb 100644 --- a/src/main/java/com/app/dto/RegisterDTO.java +++ b/src/main/java/com/app/dto/RegisterDTO.java @@ -2,10 +2,13 @@ import lombok.Builder; import lombok.Getter; +import lombok.Setter; + import java.util.Set; @Builder @Getter +@Setter public class RegisterDTO { private String username; private String password; diff --git a/src/main/java/com/app/model/UserEntity.java b/src/main/java/com/app/model/UserEntity.java index edf5110..f1a70f0 100644 --- a/src/main/java/com/app/model/UserEntity.java +++ b/src/main/java/com/app/model/UserEntity.java @@ -1,19 +1,25 @@ package com.app.model; +import jakarta.annotation.Nullable; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; - +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; import java.util.Set; +import java.util.stream.Collectors; @Builder @Getter @AllArgsConstructor @NoArgsConstructor @Entity -public class UserEntity{ +public class UserEntity implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -24,4 +30,32 @@ public class UserEntity{ joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set roles; + + //Obtención de autoridades del usuario + public Collection getAuthorities() { + return roles + .stream() + .map(role->new SimpleGrantedAuthority("ROLE_".concat(role.getName().name()))) + .collect(Collectors.toSet()); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } } diff --git a/src/main/java/com/app/security/SecurityConfig.java b/src/main/java/com/app/security/SecurityConfig.java index b0dbbf3..03e5950 100644 --- a/src/main/java/com/app/security/SecurityConfig.java +++ b/src/main/java/com/app/security/SecurityConfig.java @@ -57,7 +57,7 @@ SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager //es creado dentro de esta clase jwtAuthenticationFilter.setAuthenticationManager(authenticationManager); //Se configura el endpoint para autenticarse - jwtAuthenticationFilter.setFilterProcessesUrl("/auth/login"); + jwtAuthenticationFilter.setFilterProcessesUrl("/public/auth/login"); return http // Se deshabilita Cross-site request forgery @@ -69,13 +69,13 @@ SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager .authorizeHttpRequests( //Configuración de acceso a los endpoints auth-> { - //endpoint para registrarse permitido para todos - auth.requestMatchers("/auth/register").permitAll(); + //endpoint para poder registrarse por primera vez + auth.requestMatchers("/public/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 + //para demas endpoint solo se debe estar autenticado auth.anyRequest().authenticated(); } ) diff --git a/src/main/java/com/app/service/UserDetailsServiceImpl.java b/src/main/java/com/app/service/UserDetailsServiceImpl.java index fdf5fb0..f942a1a 100644 --- a/src/main/java/com/app/service/UserDetailsServiceImpl.java +++ b/src/main/java/com/app/service/UserDetailsServiceImpl.java @@ -3,21 +3,18 @@ import com.app.model.UserEntity; import com.app.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import java.util.Collection; -import java.util.stream.Collectors; //Esta clase sirve para retornar un usuario el cual es de tipo User de Spring Security @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired - UserRepository userRepository; + private UserRepository userRepository; + private UserEntityService userEntityService = new UserEntityServiceImpl(); + // Método para cargar un usuario con todos sus datos por medio de sus username @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { @@ -27,25 +24,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx UserEntity user = userRepository .findByUsername(username) .orElseThrow(()->new UsernameNotFoundException("User not found")); - //Colleccion la cual contiene el tipo o tipos de autoridad que tiene el usuario - Collection authorities = user - //Obtención de roles y mapeo de roles - .getRoles() - .stream(). - /*Cada rol es guardado en un objeto de tipo SimpleGrantedAuthority con el nombre de "ROLE_" y - se le concatena el rol del usuario*/ - map(role->new SimpleGrantedAuthority("ROLE_".concat(role.getName().name()))) - //Finalmente se convierte todo en una colleccion de objetos SimpleGrantedAuthority los cuales son los roles - .collect(Collectors.toSet()); // Se devuelve un usuario de tipo User (No de tipo UserEntity) esta clase de es de Spring Security - return new User( - user.getUsername(),//Usernmae del usuario - user.getPassword(),//Contraseña del usuario - true,//Usuario activo - true,//La cuenta no expira - true,//Las credenciales no expiran - true,//La cuenta no se bloquea - authorities//Roles del usuario - ); + return userEntityService.createUserSecurity(user); } } diff --git a/src/main/java/com/app/service/UserEntityService.java b/src/main/java/com/app/service/UserEntityService.java new file mode 100644 index 0000000..ea53f09 --- /dev/null +++ b/src/main/java/com/app/service/UserEntityService.java @@ -0,0 +1,10 @@ +package com.app.service; + +import com.app.dto.RegisterDTO; +import com.app.model.UserEntity; +import org.springframework.security.core.userdetails.User; + +public interface UserEntityService { + public UserEntity registerUser(RegisterDTO registerDTO); + public User createUserSecurity(UserEntity userEntity); +} diff --git a/src/main/java/com/app/service/RegisterService.java b/src/main/java/com/app/service/UserEntityServiceImpl.java similarity index 71% rename from src/main/java/com/app/service/RegisterService.java rename to src/main/java/com/app/service/UserEntityServiceImpl.java index f367e8e..1536c6c 100644 --- a/src/main/java/com/app/service/RegisterService.java +++ b/src/main/java/com/app/service/UserEntityServiceImpl.java @@ -6,20 +6,20 @@ import com.app.model.UserEntity; import com.app.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.User; 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 { +public class UserEntityServiceImpl implements UserEntityService{ @Autowired UserRepository userRepository; @Autowired PasswordEncoder passwordEncoder; + //Método para registrar un usuario nuevo public UserEntity registerUser(RegisterDTO registerDTO){ - Set roles = //obtención de roles registerDTO.getRoles() @@ -40,4 +40,17 @@ public UserEntity registerUser(RegisterDTO registerDTO){ return userRepository.save(userEntity); } + + //Método para la creación de un usuario de seguridad de Spring + public User createUserSecurity(UserEntity userEntity){ + return new User( + userEntity.getUsername(), + userEntity.getPassword(), + true, + true, + true, + true, + userEntity.getAuthorities() + ); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4246cd0..ebbaec1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,6 +3,10 @@ spring: url: ${SPRING_DATASOURCE_URL} username: ${SPRING_DATASOURCE_USERNAME} password: ${SPRING_DATASOURCE_PASSWORD} +user: + admin: + username: ${USER_ADMIN_USERNAME} + password: ${USER_ADMIN_PASSWORD} jpa: hibernate: ddl-auto: ${SPRING_JPA_HIBERNATE_DDL_AUTO}