From ea7d6f7cfc68fabba6f017818d2b30d26ec59cd0 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Fri, 9 May 2025 22:35:21 +0300 Subject: [PATCH 01/13] feat: implement all missing methods in controllers (lab-3) --- .gitignore | 3 + ApiGateway/ApplicationApiGateway/pom.xml | 20 ++++ ApiGateway/DataAccessApiGateway/pom.xml | 28 +++++ ApiGateway/PresentationApiGateway/pom.xml | 28 +++++ ApiGateway/pom.xml | 111 ++++++++++++++++++ .../Controllers/UserDTOController.java | 6 +- .../UserInteractionsController.java | 111 ++++++++++++++++++ pom.xml | 2 + 8 files changed, 306 insertions(+), 3 deletions(-) create mode 100644 ApiGateway/ApplicationApiGateway/pom.xml create mode 100644 ApiGateway/DataAccessApiGateway/pom.xml create mode 100644 ApiGateway/PresentationApiGateway/pom.xml create mode 100644 ApiGateway/pom.xml create mode 100644 Presentation/src/main/java/Presentation/Controllers/UserInteractionsController.java diff --git a/.gitignore b/.gitignore index d41fd5e..46db3d3 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,9 @@ DataAccess.iml Presentation.iml Application.iml target +DataAccessApiGateway.iml +ApiGateway.iml +ApplicationApiGateway.iml ### VS Code ### .vscode/ diff --git a/ApiGateway/ApplicationApiGateway/pom.xml b/ApiGateway/ApplicationApiGateway/pom.xml new file mode 100644 index 0000000..cc671ed --- /dev/null +++ b/ApiGateway/ApplicationApiGateway/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + com.example + ApiGateway + 1.0-SNAPSHOT + + + ApplicationApiGateway + + + 21 + 21 + UTF-8 + + + \ No newline at end of file diff --git a/ApiGateway/DataAccessApiGateway/pom.xml b/ApiGateway/DataAccessApiGateway/pom.xml new file mode 100644 index 0000000..593fa2b --- /dev/null +++ b/ApiGateway/DataAccessApiGateway/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + com.example + ApiGateway + 1.0-SNAPSHOT + + + DataAccessApiGateway + + + 21 + 21 + UTF-8 + + + + + com.example + ApplicationApiGateway + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/ApiGateway/PresentationApiGateway/pom.xml b/ApiGateway/PresentationApiGateway/pom.xml new file mode 100644 index 0000000..de82c16 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + com.example + ApiGateway + 1.0-SNAPSHOT + + + PresentationApiGateway + + + 21 + 21 + UTF-8 + + + + + com.example + DataAccessApiGateway + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/ApiGateway/pom.xml b/ApiGateway/pom.xml new file mode 100644 index 0000000..64a2731 --- /dev/null +++ b/ApiGateway/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + com.example + lim0sha + 1.0-SNAPSHOT + + + ApiGateway + pom + + ApplicationApiGateway + PresentationApiGateway + + + + 21 + 21 + UTF-8 + + + + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + + + + + org.projectlombok + lombok + 1.18.30 + provided + + + + + org.springframework.boot + spring-boot-starter + + + + + org.postgresql + postgresql + runtime + + + + + org.springframework.boot + spring-boot-starter-security + + + + + + + + io.jsonwebtoken + jjwt-jackson + 0.12.3 + runtime + + + + + io.jsonwebtoken + jjwt-impl + 0.12.3 + runtime + + + + + io.jsonwebtoken + jjwt-api + 0.12.3 + + + + + io.github.cdimascio + dotenv-java + 2.2.4 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + \ No newline at end of file diff --git a/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java b/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java index b008660..0faeafc 100644 --- a/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java +++ b/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java @@ -45,7 +45,7 @@ public ResponseEntity getUserById(@Parameter(description = "ID поль @ApiResponse(responseCode = "201", description = "Пользователь создан"), @ApiResponse(responseCode = "400", description = "Некорректные данные") }) - @PostMapping + @PostMapping("/create") public ResponseEntity createUser(@RequestBody User user) { UserResult result = baseController.CreateUser(user); if (result instanceof UserResult.Success) { @@ -61,7 +61,7 @@ public ResponseEntity createUser(@RequestBody User user) { @ApiResponse(responseCode = "404", description = "Пользователь не найден"), @ApiResponse(responseCode = "400", description = "Некорректные данные") }) - @PutMapping("/{id}") + @PutMapping("/update/{id}") public ResponseEntity updateUser(@Parameter(description = "ID пользователя") @PathVariable int id, @RequestBody User user) { User existingUser = baseController.GetUserById(id); if (existingUser == null) { @@ -80,7 +80,7 @@ public ResponseEntity updateUser(@Parameter(description = "ID пользов @ApiResponse(responseCode = "204", description = "Пользователь удален"), @ApiResponse(responseCode = "404", description = "Пользователь не найден") }) - @DeleteMapping("/{id}") + @DeleteMapping("/delete/{id}") public ResponseEntity deleteUser(@Parameter(description = "ID пользователя") @PathVariable int id) { User user = baseController.GetUserById(id); if (user == null) { diff --git a/Presentation/src/main/java/Presentation/Controllers/UserInteractionsController.java b/Presentation/src/main/java/Presentation/Controllers/UserInteractionsController.java new file mode 100644 index 0000000..53fe9be --- /dev/null +++ b/Presentation/src/main/java/Presentation/Controllers/UserInteractionsController.java @@ -0,0 +1,111 @@ +package Presentation.Controllers; + +import Application.Models.Entities.BankAccount; +import Application.ResultTypes.OperationResult; +import Presentation.Interfaces.IBaseController; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/interactions") +public class UserInteractionsController { + + private final IBaseController baseController; + + @Autowired + public UserInteractionsController(IBaseController baseController) { + this.baseController = baseController; + } + + @Operation(summary = "Добавить друга", description = "Добавляет пользователя в друзья") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Друг добавлен"), + @ApiResponse(responseCode = "400", description = "Ошибка добавления") + }) + @PostMapping("/friends/add/{userId}/{otherId}") + public ResponseEntity addFriend( + @PathVariable int userId, + @PathVariable int otherId + ) { + baseController.AddFriend(userId, otherId); + return ResponseEntity.ok("Friend added."); + } + + @Operation(summary = "Удалить друга", description = "Удаляет пользователя из друзей") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Друг удалён"), + @ApiResponse(responseCode = "400", description = "Ошибка удаления") + }) + @PostMapping("/friends/remove/{userId}/{otherId}") + public ResponseEntity removeFriend( + @PathVariable int userId, + @PathVariable int otherId + ) { + baseController.RemoveFriend(userId, otherId); + return ResponseEntity.ok("Friend removed."); + } + + @Operation(summary = "Пополнить счёт", description = "Осуществляет пополнение счёта") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Пополнение выполнено"), + @ApiResponse(responseCode = "400", description = "Ошибка пополнения") + }) + @PostMapping("/deposit/{accountId}/{amount}") + public ResponseEntity deposit( + @PathVariable int accountId, + @PathVariable double amount + ) { + BankAccount account = baseController.GetBankAccountById(accountId); + OperationResult result = baseController.Deposit(account, amount); + + if (result instanceof OperationResult.Success) { + return ResponseEntity.ok("Deposit successful."); + } + return ResponseEntity.badRequest().body(result.toString()); + } + + @Operation(summary = "Снять со счёта", description = "Осуществляет снятие средств со счёта") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Снятие выполнено"), + @ApiResponse(responseCode = "400", description = "Ошибка снятия") + }) + @PostMapping("/withdraw/{accountId}/{amount}") + public ResponseEntity withdraw( + @PathVariable int accountId, + @PathVariable double amount + ) { + BankAccount account = baseController.GetBankAccountById(accountId); + OperationResult result = baseController.Withdraw(account, amount); + + if (result instanceof OperationResult.Success) { + return ResponseEntity.ok("Withdrawal successful."); + } + return ResponseEntity.badRequest().body(result.toString()); + } + + @Operation(summary = "Перевод между счетами", description = "Выполняет перевод между счетами с учётом комиссии") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Перевод выполнен"), + @ApiResponse(responseCode = "400", description = "Ошибка перевода") + }) + @PostMapping("/transfer/{fromAccountId}/{toAccountId}/{amount}") + public ResponseEntity transfer( + @PathVariable int fromAccountId, + @PathVariable int toAccountId, + @PathVariable double amount + ) { + BankAccount fromAccount = baseController.GetBankAccountById(fromAccountId); + BankAccount toAccount = baseController.GetBankAccountById(toAccountId); + + OperationResult result = baseController.Transfer(fromAccount, toAccount, amount); + + if (result instanceof OperationResult.Success) { + return ResponseEntity.ok("Transfer successful."); + } + return ResponseEntity.badRequest().body(result.toString()); + } +} diff --git a/pom.xml b/pom.xml index 3b6964c..b7f3d90 100644 --- a/pom.xml +++ b/pom.xml @@ -96,6 +96,8 @@ Application DataAccess Presentation + ApiGateway + ApiGateway/DataAccessApiGateway From 9011c7b6e1e0030194e85330c21a9e90f42706f3 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Fri, 9 May 2025 22:37:01 +0300 Subject: [PATCH 02/13] feat: implement simple login page; Admin/Client model; Security configs and ApiGateway controllers --- .../src/main/java/Models/Entities/User.java | 36 +++++ .../src/main/java/Models/Enums/Role.java | 6 + .../java/Repositories/IUserRepository.java | 12 ++ .../Services/CustomUserDetailService.java | 34 +++++ .../src/main/java/Services/UserService.java | 30 ++++ .../main/java/Configs/RestTemplateConfig.java | 13 ++ .../src/main/java/Configs/SecurityConfig.java | 64 +++++++++ .../src/main/java/Console/ApiGatewayApp.java | 23 +++ .../java/Console/PasswordEncoderTest.java | 12 ++ .../java/Controllers/AdminController.java | 131 ++++++++++++++++++ .../main/java/Controllers/AuthController.java | 51 +++++++ .../java/Controllers/ClientController.java | 115 +++++++++++++++ .../src/main/java/DTO/BankAccountDTO.java | 15 ++ .../src/main/java/DTO/OperationDTO.java | 15 ++ .../src/main/java/DTO/UserDTO.java | 21 +++ .../src/main/java/JWT/JwtAuthFilter.java | 59 ++++++++ .../src/main/java/JWT/JwtUtil.java | 81 +++++++++++ .../src/main/java/Requests/UserRequest.java | 20 +++ .../src/main/resources/application.yml | 34 +++++ 19 files changed, 772 insertions(+) create mode 100644 ApiGateway/ApplicationApiGateway/src/main/java/Models/Entities/User.java create mode 100644 ApiGateway/ApplicationApiGateway/src/main/java/Models/Enums/Role.java create mode 100644 ApiGateway/DataAccessApiGateway/src/main/java/Repositories/IUserRepository.java create mode 100644 ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java create mode 100644 ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Configs/RestTemplateConfig.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Console/ApiGatewayApp.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Console/PasswordEncoderTest.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/DTO/BankAccountDTO.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/DTO/OperationDTO.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/DTO/UserDTO.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/resources/application.yml diff --git a/ApiGateway/ApplicationApiGateway/src/main/java/Models/Entities/User.java b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Entities/User.java new file mode 100644 index 0000000..8eca4f7 --- /dev/null +++ b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Entities/User.java @@ -0,0 +1,36 @@ +package Models.Entities; + +import Models.Enums.Role; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "users") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Getter + private Long id; + + @Column(unique = true, nullable = false) + private String username; + + @Column(nullable = false) + private String password; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + @Getter + private Role role; + + public User(String username, String pw, Role role) { + this.username = username; + this.password = pw; + this.role = role; + } +} diff --git a/ApiGateway/ApplicationApiGateway/src/main/java/Models/Enums/Role.java b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Enums/Role.java new file mode 100644 index 0000000..e1f4b5a --- /dev/null +++ b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Enums/Role.java @@ -0,0 +1,6 @@ +package Models.Enums; + +public enum Role { + ADMIN, + CLIENT +} diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/IUserRepository.java b/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/IUserRepository.java new file mode 100644 index 0000000..b40f39c --- /dev/null +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/IUserRepository.java @@ -0,0 +1,12 @@ +package Repositories; + +import Models.Entities.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface IUserRepository extends JpaRepository { + Optional findByUsername(String username); +} diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java new file mode 100644 index 0000000..adbb5f9 --- /dev/null +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java @@ -0,0 +1,34 @@ +package Services; + +import Repositories.IUserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +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; + +@Service +public class CustomUserDetailService implements UserDetailsService { + private final IUserRepository userRepository; + + @Autowired + public CustomUserDetailService(IUserRepository repository) { + this.userRepository = repository; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + var user = userRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User is not found.")); + + String role = "ROLE_" + user.getRole().toString(); + + return org.springframework.security.core.userdetails.User + .withUsername(user.getUsername()) + .password(user.getPassword()) + .authorities(new SimpleGrantedAuthority(role)) + .build(); + } + +} diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java new file mode 100644 index 0000000..254d09f --- /dev/null +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java @@ -0,0 +1,30 @@ +package Services; + +import Models.Entities.User; +import Models.Enums.Role; +import Repositories.IUserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + private final IUserRepository userRepository; + private final PasswordEncoder encoder; + + @Autowired + public UserService(IUserRepository repository, PasswordEncoder passwordEncoder) { + this.userRepository = repository; + this.encoder = passwordEncoder; + } + + public User CreateUser(String username, String password, Role role) { + String pwHash = encoder.encode(password); + User user = new User(username, pwHash, role); + return userRepository.save(user); + } + + public User FindUserByUsername(String username) { + return userRepository.findByUsername(username).orElse(null); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Configs/RestTemplateConfig.java b/ApiGateway/PresentationApiGateway/src/main/java/Configs/RestTemplateConfig.java new file mode 100644 index 0000000..802a70f --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Configs/RestTemplateConfig.java @@ -0,0 +1,13 @@ +package Configs; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java new file mode 100644 index 0000000..af1aa77 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java @@ -0,0 +1,64 @@ +package Configs; + +import JWT.JwtAuthFilter; +import Services.CustomUserDetailService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableMethodSecurity +public class SecurityConfig { + + private final JwtAuthFilter jwtAuthFilter; + private final CustomUserDetailService userDetailService; + + public SecurityConfig(JwtAuthFilter jwtAuthFilter, CustomUserDetailService userDetailService) { + this.jwtAuthFilter = jwtAuthFilter; + this.userDetailService = userDetailService; + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/login", "/login/**").permitAll() + .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN") + .requestMatchers("/client/**").hasAuthority("ROLE_CLIENT") + .anyRequest().authenticated() + ) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(userDetailService); + provider.setPasswordEncoder(passwordEncoder()); + return provider; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + return config.getAuthenticationManager(); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Console/ApiGatewayApp.java b/ApiGateway/PresentationApiGateway/src/main/java/Console/ApiGatewayApp.java new file mode 100644 index 0000000..05d88fb --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Console/ApiGatewayApp.java @@ -0,0 +1,23 @@ +package Console; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@SpringBootApplication(scanBasePackages = { + "Controllers", + "Configs", + "JWT", + "Services", + "Repositories", + "Models.Entities", + "Models.Enums" +}) +@EnableJpaRepositories(basePackages = "Repositories") +@EntityScan(basePackages = "Models.Entities") +public class ApiGatewayApp { + public static void main(String[] args) { + SpringApplication.run(ApiGatewayApp.class, args); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Console/PasswordEncoderTest.java b/ApiGateway/PresentationApiGateway/src/main/java/Console/PasswordEncoderTest.java new file mode 100644 index 0000000..cf26fa1 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Console/PasswordEncoderTest.java @@ -0,0 +1,12 @@ +package Console; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class PasswordEncoderTest { + public static void main(String[] args) { + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + String password = "admin123"; + String encodedPassword = encoder.encode(password); + System.out.println(encodedPassword); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java new file mode 100644 index 0000000..3432620 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java @@ -0,0 +1,131 @@ +package Controllers; + +import DTO.BankAccountDTO; +import DTO.OperationDTO; +import DTO.UserDTO; +import Requests.UserRequest; +import Services.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; + +import java.util.List; + +@RestController +@RequestMapping("/admin") +public class AdminController { + private final RestTemplate restTemplate; + private final UserService userService; + + @Autowired + public AdminController(RestTemplate restTemplate, UserService userService) { + this.restTemplate = restTemplate; + this.userService = userService; + } + + // 1 - Создание пользователя + // 2 - Создание администратора + @PreAuthorize("hasRole('ADMIN')") + @PostMapping("/users/create") + public ResponseEntity CreateUser(@RequestBody UserRequest userRequest) { + try { + String url = "http://localhost:8080/users/create"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity requestHttpEntity = new HttpEntity<>(userRequest, headers); + + ResponseEntity response = restTemplate.postForEntity(url, requestHttpEntity, String.class); + if (!response.getStatusCode().is2xxSuccessful()) { + return ResponseEntity.status(response.getStatusCode()) + .body("Failed to create new user."); + } + + System.out.println("User created successfully."); + userService.CreateUser(userRequest.getUsername(), userRequest.getPassword(), userRequest.getRole()); + return ResponseEntity.ok("Localhost user created."); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error creating user: " + e.getMessage()); + } + } + + // 3 - Получение информации о всех пользователях с фильтрацией по гендеру и цвету волос + @PreAuthorize("hasRole('ADMIN')") + @GetMapping("/users") + public ResponseEntity> GetAllUsersFiltered( + @RequestParam(required = false) String sex, + @RequestParam(required = false) String hairColor + ) { + String url = "http://localhost:8080/data/users?sex=" + (sex != null ? sex : "") + + "&hairColor=" + (hairColor != null ? hairColor : ""); + + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.GET, null, new ParameterizedTypeReference>() {} + ); + + List users = response.getBody(); + return ResponseEntity.ok(users); + } + + // 4 - Получение информации о пользователе по его идентификатору + @PreAuthorize("hasRole('ADMIN')") + @GetMapping("/users/{id}") + public ResponseEntity GetUserById(@PathVariable int id) { + String url = "http://localhost:8080/users/" + id; + return restTemplate.getForEntity(url, UserDTO.class); + } + + + // 5 - Получение информации о всех счётах пользователей + @PreAuthorize("hasRole('ADMIN')") + @GetMapping("/bankaccounts") + public ResponseEntity> GetAllBankAccounts() { + String url = "http://localhost:8080/data/accounts"; + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} + ); + return ResponseEntity.ok(response.getBody()); + } + + // 6 - Получение информации о счетах по идентификатору пользователя + @PreAuthorize("hasRole('ADMIN')") + @GetMapping("/bankaccounts/user/{userId}") + public ResponseEntity GetAccountsByUserId(@PathVariable int userId) { + String url = "http://localhost:8080/data/users/" + userId + "/accounts"; + ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); + return ResponseEntity.ok(response.getBody()); + } + + // 7 - Получение информации о счёте со списком его операций по идентификатору счёта + @PreAuthorize("hasRole('ADMIN')") + @GetMapping("/bankaccounts/{id}/operations") + public ResponseEntity> getOperationsByAccountId( + @PathVariable int id, + @RequestParam(required = false) String type + ) { + String url = "http://localhost:8080/data/operations?accountId=" + id; + if (type != null && !type.isEmpty()) { + url += "&type=" + type; + } + + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.GET, null, new ParameterizedTypeReference>() {} + ); + + return ResponseEntity.ok(response.getBody()); + } + + // 8 - Деавторизация + @PreAuthorize("isAuthenticated()") + @PostMapping("/logout") + public ResponseEntity Logout() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return ResponseEntity.ok("Admin " + authentication.getName() + " logged out."); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java new file mode 100644 index 0000000..427f3b8 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java @@ -0,0 +1,51 @@ +package Controllers; + +import JWT.JwtUtil; +import Requests.UserRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.web.bind.annotation.*; + +@RestController +public class AuthController { + + private final AuthenticationManager authenticationManager; + private final JwtUtil jwtUtil; + private final UserDetailsService userDetailsService; + + @Autowired + public AuthController(AuthenticationManager authenticationManager, JwtUtil jwtUtil, UserDetailsService userDetailsService) { + this.authenticationManager = authenticationManager; + this.jwtUtil = jwtUtil; + this.userDetailsService = userDetailsService; + } + + + @GetMapping("/login") + public ResponseEntity getLoginInfo() { + return ResponseEntity.status(HttpStatus.OK).body("Please send a POST request to /login with your credentials."); + } + + + @PostMapping("/login") + public ResponseEntity login(@RequestBody UserRequest request) { + try { + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()) + ); + + UserDetails userDetails = userDetailsService.loadUserByUsername(request.getUsername()); + String token = jwtUtil.generateToken(userDetails); + + return ResponseEntity.ok(token); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password"); + } + } +} \ No newline at end of file diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java new file mode 100644 index 0000000..21dcb6b --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java @@ -0,0 +1,115 @@ +package Controllers; + +import DTO.BankAccountDTO; +import DTO.UserDTO; +import Services.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; + +@RestController +@RequestMapping("/client") +public class ClientController { + + private final RestTemplate restTemplate; + private final UserService userService; + + @Autowired + public ClientController(RestTemplate restTemplate, UserService userService) { + this.restTemplate = restTemplate; + this.userService = userService; + } + + private Long getCurrentUserId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = authentication.getName(); + return userService.FindUserByUsername(username).getId(); + } + + // 1 - Получение информации о себе + @PreAuthorize("hasRole('CLIENT')") + @GetMapping("/me") + public ResponseEntity getCurrentUser() { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/users/" + userId; + return restTemplate.getForEntity(url, UserDTO.class); + } + + // 2 - Получение своих счетов + @PreAuthorize("hasRole('CLIENT')") + @GetMapping("/accounts") + public ResponseEntity getMyAccounts() { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/data/users/" + userId + "/accounts"; + ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); + return ResponseEntity.ok(response.getBody()); + } + + // 3 - Получение конкретного счёта + @PreAuthorize("hasRole('CLIENT')") + @GetMapping("/accounts/{id}") + public ResponseEntity getAccountById(@PathVariable int id) { + String url = "http://localhost:8080/data/accounts/" + id; + ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO.class); + return ResponseEntity.ok(response.getBody()); + } + + // 4 - Добавление другого пользователя в друзья + @PreAuthorize("hasRole('CLIENT')") + @PostMapping("/friends/add/{otherId}") + public ResponseEntity addFriend(@PathVariable int otherId) { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId; + return restTemplate.postForEntity(url, null, String.class); + } + + // 5 - Удаление пользователя из друзей + @PreAuthorize("hasRole('CLIENT')") + @PostMapping("/friends/remove/{otherId}") + public ResponseEntity removeFriend(@PathVariable int otherId) { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId; + return restTemplate.postForEntity(url, null, String.class); + } + + // 6.1 - Пополнение + @PreAuthorize("hasRole('CLIENT')") + @PostMapping("/accounts/{accountId}/deposit/{amount}") + public ResponseEntity deposit(@PathVariable int accountId, @PathVariable double amount) { + String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount; + return restTemplate.postForEntity(url, null, String.class); + } + + // 6.2 - Снятие + @PreAuthorize("hasRole('CLIENT')") + @PostMapping("/accounts/{accountId}/withdraw/{amount}") + public ResponseEntity withdraw(@PathVariable int accountId, @PathVariable double amount) { + String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount; + return restTemplate.postForEntity(url, null, String.class); + } + + // 6.3 - Перевод + @PreAuthorize("hasRole('CLIENT')") + @PostMapping("/accounts/transfer/{fromId}/{toId}/{amount}") + public ResponseEntity transfer( + @PathVariable int fromId, + @PathVariable int toId, + @PathVariable double amount + ) { + String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount; + return restTemplate.postForEntity(url, null, String.class); + } + + // 7 - Деавторизация + @PreAuthorize("isAuthenticated()") + @PostMapping("/logout") + public ResponseEntity logout() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = authentication.getName(); + return ResponseEntity.ok("Client " + username + " logged out."); + } +} \ No newline at end of file diff --git a/ApiGateway/PresentationApiGateway/src/main/java/DTO/BankAccountDTO.java b/ApiGateway/PresentationApiGateway/src/main/java/DTO/BankAccountDTO.java new file mode 100644 index 0000000..c795c80 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/DTO/BankAccountDTO.java @@ -0,0 +1,15 @@ +package DTO; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class BankAccountDTO { + private Integer id; + private Double balance; + private String userLogin; + private Integer userId; +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/DTO/OperationDTO.java b/ApiGateway/PresentationApiGateway/src/main/java/DTO/OperationDTO.java new file mode 100644 index 0000000..f9c1e1e --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/DTO/OperationDTO.java @@ -0,0 +1,15 @@ +package DTO; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class OperationDTO { + private Integer id; + private String type; + private Double amount; + private Integer accountId; +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/DTO/UserDTO.java b/ApiGateway/PresentationApiGateway/src/main/java/DTO/UserDTO.java new file mode 100644 index 0000000..fbe6357 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/DTO/UserDTO.java @@ -0,0 +1,21 @@ +package DTO; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +public class UserDTO { + private Integer id; + private String login; + private String name; + private Integer age; + private String sex; + private String hairType; + private List friendIds; + private List bankAccountIds; +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java new file mode 100644 index 0000000..6fb169f --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java @@ -0,0 +1,59 @@ +package JWT; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class JwtAuthFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + private final UserDetailsService userDetailsService; + + @Autowired + public JwtAuthFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) { + this.jwtUtil = jwtUtil; + this.userDetailsService = userDetailsService; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + String path = request.getServletPath(); + if (path.equals("/login")) { + filterChain.doFilter(request, response); + return; + } + + String authHeader = request.getHeader("Authorization"); + String username = null; + String jwt = null; + + if (authHeader != null && authHeader.startsWith("Bearer ")) { + jwt = authHeader.substring(7); + username = jwtUtil.extractUsername(jwt); + } + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); + if (jwtUtil.validateToken(jwt, userDetails)) { + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + + filterChain.doFilter(request, response); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java new file mode 100644 index 0000000..9cefce7 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java @@ -0,0 +1,81 @@ +package JWT; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Component +public class JwtUtil { + + @Value("${jwt.secret}") + private String secret; + + @Value("${jwt.expiration}") + private Long expiration; + + private SecretKey getSecretKey() { + return Keys.hmacShaKeyFor(secret.getBytes()); + } + + public String extractUsername(String token) { + return extractClaim(token, Claims::getSubject); + } + + public Date extractExpiration(String token) { + return extractClaim(token, Claims::getExpiration); + } + + public T extractClaim(String token, Function claimsResolver) { + final Claims claims = extractAllClaims(token); + return claimsResolver.apply(claims); + } + + private Claims extractAllClaims(String token) { + return Jwts.parser() + .setSigningKey(getSecretKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + private Boolean isTokenExpired(String token) { + return extractExpiration(token).before(new Date()); + } + + public String generateToken(UserDetails userDetails) { + Map claims = new HashMap<>(); + + claims.put("role", userDetails.getAuthorities().stream() + .map(grantedAuthority -> grantedAuthority.getAuthority()) + .collect(Collectors.toList())); + return createToken(claims, userDetails.getUsername()); + } + + private String createToken(Map claims, String subject) { + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + expiration)) + .signWith(getSecretKey(), SignatureAlgorithm.HS256) + .compact(); + } + + public Boolean validateToken(String token, UserDetails userDetails) { + final String username = extractUsername(token); + return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } + + public List extractRoles(String token) { + return extractClaim(token, claims -> claims.get("role", List.class)); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java new file mode 100644 index 0000000..5c44008 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java @@ -0,0 +1,20 @@ +package Requests; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import Models.Enums.Role; + +@Getter +@AllArgsConstructor +public class UserRequest { + + private final String username; + private final String password; + private final Role role; + + public UserRequest(String username, String password) { + this.username = username; + this.password = password; + this.role = null; + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml new file mode 100644 index 0000000..7c649fa --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml @@ -0,0 +1,34 @@ +spring: + application: + name: ApiGateway + + main: + allow-bean-definition-overriding: true + + flyway: + enabled: false + + datasource: + url: jdbc:postgresql://localhost:5432/JavaLabsApiDb + username: postgres + password: limosha + driver-class-name: org.postgresql.Driver + + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + hibernate: + ddl-auto: validate + show-sql: true + properties: + hibernate.format_sql: true + +# JWT Configuration +jwt: + secret: my_super_secret_key_123456 + expiration: 86400000 # 1 day + +server: + error: + include-message: always + include-binding-errors: always + include-stacktrace: never \ No newline at end of file From b114a1f3acaa75b094dea9a23761fa8bdc41800b Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Fri, 9 May 2025 22:47:21 +0300 Subject: [PATCH 03/13] fix: update application.yml to remove warns --- .../src/main/resources/application.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml index 7c649fa..93679db 100644 --- a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml +++ b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml @@ -15,12 +15,17 @@ spring: driver-class-name: org.postgresql.Driver jpa: - database-platform: org.hibernate.dialect.PostgreSQLDialect hibernate: ddl-auto: validate show-sql: true + open-in-view: false properties: - hibernate.format_sql: true + hibernate: + format_sql: true + + thymeleaf: + enabled: false + check-template-location: false # JWT Configuration jwt: @@ -31,4 +36,4 @@ server: error: include-message: always include-binding-errors: always - include-stacktrace: never \ No newline at end of file + include-stacktrace: never From 87d8798216edfe56c587587556739729223165af Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 00:09:11 +0300 Subject: [PATCH 04/13] feat: update jwt token logic --- .../src/main/java/Configs/SecurityConfig.java | 21 ++++++++++--------- .../src/main/java/JWT/JwtAuthFilter.java | 4 ++-- .../src/main/java/Responses/JwtResponse.java | 17 +++++++++++++++ 3 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Responses/JwtResponse.java diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java index af1aa77..3b2aa1b 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java @@ -5,9 +5,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -16,6 +15,8 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import java.util.List; + @Configuration @EnableMethodSecurity public class SecurityConfig { @@ -34,10 +35,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .csrf(csrf -> csrf.disable()) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth - .requestMatchers("/login", "/login/**").permitAll() - .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN") + .requestMatchers("/login", "/auth/**").permitAll() + .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN") .requestMatchers("/client/**").hasAuthority("ROLE_CLIENT") - .anyRequest().authenticated() + .anyRequest().authenticated() ) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); @@ -45,7 +46,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti } @Bean - public AuthenticationProvider authenticationProvider() { + public DaoAuthenticationProvider daoAuthenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userDetailService); provider.setPasswordEncoder(passwordEncoder()); @@ -53,12 +54,12 @@ public AuthenticationProvider authenticationProvider() { } @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); + public AuthenticationManager authenticationManager(DaoAuthenticationProvider daoAuthProvider) { + return new ProviderManager(List.of(daoAuthProvider)); } @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { - return config.getAuthenticationManager(); + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); } } diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java index 6fb169f..28bf617 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java @@ -31,8 +31,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse throws ServletException, IOException { String path = request.getServletPath(); - if (path.equals("/login")) { - filterChain.doFilter(request, response); + if (path.startsWith("/auth") || path.equals("/login")) { + filterChain.doFilter(request, response); return; } diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Responses/JwtResponse.java b/ApiGateway/PresentationApiGateway/src/main/java/Responses/JwtResponse.java new file mode 100644 index 0000000..3d5abad --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Responses/JwtResponse.java @@ -0,0 +1,17 @@ +package Responses; + +public class JwtResponse { + private String token; + + public JwtResponse(String token) { + this.token = token; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} From 6b3fc69a63ff1676389701053da312435b400ca2 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 00:18:52 +0300 Subject: [PATCH 05/13] fix: client can view only his accounts; feat: wired AdminController and ClientController methods with try-catch --- .../java/Controllers/AdminController.java | 119 ++++++++------- .../java/Controllers/ClientController.java | 144 ++++++++++++------ 2 files changed, 162 insertions(+), 101 deletions(-) diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java index 3432620..ad341b0 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java @@ -28,104 +28,109 @@ public AdminController(RestTemplate restTemplate, UserService userService) { this.userService = userService; } - // 1 - Создание пользователя - // 2 - Создание администратора @PreAuthorize("hasRole('ADMIN')") @PostMapping("/users/create") public ResponseEntity CreateUser(@RequestBody UserRequest userRequest) { try { - String url = "http://localhost:8080/users/create"; - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity requestHttpEntity = new HttpEntity<>(userRequest, headers); - - ResponseEntity response = restTemplate.postForEntity(url, requestHttpEntity, String.class); - if (!response.getStatusCode().is2xxSuccessful()) { - return ResponseEntity.status(response.getStatusCode()) - .body("Failed to create new user."); - } - - System.out.println("User created successfully."); userService.CreateUser(userRequest.getUsername(), userRequest.getPassword(), userRequest.getRole()); - return ResponseEntity.ok("Localhost user created."); + return ResponseEntity.ok("User created successfully."); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error creating user: " + e.getMessage()); } } - // 3 - Получение информации о всех пользователях с фильтрацией по гендеру и цвету волос @PreAuthorize("hasRole('ADMIN')") @GetMapping("/users") - public ResponseEntity> GetAllUsersFiltered( + public ResponseEntity GetAllUsersFiltered( @RequestParam(required = false) String sex, @RequestParam(required = false) String hairColor ) { - String url = "http://localhost:8080/data/users?sex=" + (sex != null ? sex : "") + - "&hairColor=" + (hairColor != null ? hairColor : ""); - - ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, null, new ParameterizedTypeReference>() {} - ); + try { + String url = "http://localhost:8080/data/users?sex=" + (sex != null ? sex : "") + + "&hairColor=" + (hairColor != null ? hairColor : ""); - List users = response.getBody(); - return ResponseEntity.ok(users); + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} + ); + return ResponseEntity.ok(response.getBody()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving users: " + e.getMessage()); + } } - // 4 - Получение информации о пользователе по его идентификатору @PreAuthorize("hasRole('ADMIN')") @GetMapping("/users/{id}") - public ResponseEntity GetUserById(@PathVariable int id) { - String url = "http://localhost:8080/users/" + id; - return restTemplate.getForEntity(url, UserDTO.class); + public ResponseEntity GetUserById(@PathVariable int id) { + try { + String url = "http://localhost:8080/users/" + id; + return restTemplate.getForEntity(url, UserDTO.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving user: " + e.getMessage()); + } } - - // 5 - Получение информации о всех счётах пользователей @PreAuthorize("hasRole('ADMIN')") @GetMapping("/bankaccounts") - public ResponseEntity> GetAllBankAccounts() { - String url = "http://localhost:8080/data/accounts"; - ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} - ); - return ResponseEntity.ok(response.getBody()); + public ResponseEntity GetAllBankAccounts() { + try { + String url = "http://localhost:8080/data/accounts"; + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} + ); + return ResponseEntity.ok(response.getBody()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving accounts: " + e.getMessage()); + } } - // 6 - Получение информации о счетах по идентификатору пользователя @PreAuthorize("hasRole('ADMIN')") @GetMapping("/bankaccounts/user/{userId}") public ResponseEntity GetAccountsByUserId(@PathVariable int userId) { - String url = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); - return ResponseEntity.ok(response.getBody()); + try { + String url = "http://localhost:8080/data/users/" + userId + "/accounts"; + ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); + return ResponseEntity.ok(response.getBody()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving accounts for user: " + e.getMessage()); + } } - // 7 - Получение информации о счёте со списком его операций по идентификатору счёта @PreAuthorize("hasRole('ADMIN')") @GetMapping("/bankaccounts/{id}/operations") - public ResponseEntity> getOperationsByAccountId( + public ResponseEntity getOperationsByAccountId( @PathVariable int id, @RequestParam(required = false) String type ) { - String url = "http://localhost:8080/data/operations?accountId=" + id; - if (type != null && !type.isEmpty()) { - url += "&type=" + type; - } - - ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, null, new ParameterizedTypeReference>() {} - ); + try { + String url = "http://localhost:8080/data/operations?accountId=" + id; + if (type != null && !type.isEmpty()) { + url += "&type=" + type; + } - return ResponseEntity.ok(response.getBody()); + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} + ); + return ResponseEntity.ok(response.getBody()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving operations: " + e.getMessage()); + } } - // 8 - Деавторизация @PreAuthorize("isAuthenticated()") @PostMapping("/logout") - public ResponseEntity Logout() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - return ResponseEntity.ok("Admin " + authentication.getName() + " logged out."); + public ResponseEntity Logout() { + try { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return ResponseEntity.ok("Admin " + authentication.getName() + " logged out."); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error during logout: " + e.getMessage()); + } } } diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java index 21dcb6b..24f7649 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java @@ -11,6 +11,8 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; +import java.util.Objects; + @RestController @RequestMapping("/client") public class ClientController { @@ -30,86 +32,140 @@ private Long getCurrentUserId() { return userService.FindUserByUsername(username).getId(); } - // 1 - Получение информации о себе @PreAuthorize("hasRole('CLIENT')") @GetMapping("/me") - public ResponseEntity getCurrentUser() { - Long userId = getCurrentUserId(); - String url = "http://localhost:8080/users/" + userId; - return restTemplate.getForEntity(url, UserDTO.class); + public ResponseEntity getCurrentUser() { + try { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/users/" + userId; + return restTemplate.getForEntity(url, UserDTO.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving current user: " + e.getMessage()); + } } - // 2 - Получение своих счетов @PreAuthorize("hasRole('CLIENT')") @GetMapping("/accounts") - public ResponseEntity getMyAccounts() { - Long userId = getCurrentUserId(); - String url = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); - return ResponseEntity.ok(response.getBody()); + public ResponseEntity getMyAccounts() { + try { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/data/users/" + userId + "/accounts"; + ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); + return ResponseEntity.ok(response.getBody()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving accounts: " + e.getMessage()); + } } - // 3 - Получение конкретного счёта @PreAuthorize("hasRole('CLIENT')") @GetMapping("/accounts/{id}") - public ResponseEntity getAccountById(@PathVariable int id) { - String url = "http://localhost:8080/data/accounts/" + id; - ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO.class); - return ResponseEntity.ok(response.getBody()); + public ResponseEntity getAccountById(@PathVariable int id) { + try { + Long userId = getCurrentUserId(); + + String userAccountsUrl = "http://localhost:8080/data/users/" + userId + "/accounts"; + ResponseEntity accountsResponse = restTemplate.getForEntity(userAccountsUrl, BankAccountDTO[].class); + + boolean ownsAccount = false; + if (accountsResponse.getStatusCode().is2xxSuccessful()) { + for (BankAccountDTO account : Objects.requireNonNull(accountsResponse.getBody())) { + if (account.getId() == id) { + ownsAccount = true; + break; + } + } + } + + if (!ownsAccount) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Access denied to account."); + } + + String url = "http://localhost:8080/data/accounts/" + id; + return restTemplate.getForEntity(url, BankAccountDTO.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error retrieving account: " + e.getMessage()); + } } - // 4 - Добавление другого пользователя в друзья @PreAuthorize("hasRole('CLIENT')") @PostMapping("/friends/add/{otherId}") - public ResponseEntity addFriend(@PathVariable int otherId) { - Long userId = getCurrentUserId(); - String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId; - return restTemplate.postForEntity(url, null, String.class); + public ResponseEntity addFriend(@PathVariable int otherId) { + try { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId; + return restTemplate.postForEntity(url, null, String.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error adding friend: " + e.getMessage()); + } } - // 5 - Удаление пользователя из друзей @PreAuthorize("hasRole('CLIENT')") @PostMapping("/friends/remove/{otherId}") - public ResponseEntity removeFriend(@PathVariable int otherId) { - Long userId = getCurrentUserId(); - String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId; - return restTemplate.postForEntity(url, null, String.class); + public ResponseEntity removeFriend(@PathVariable int otherId) { + try { + Long userId = getCurrentUserId(); + String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId; + return restTemplate.postForEntity(url, null, String.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error removing friend: " + e.getMessage()); + } } - // 6.1 - Пополнение @PreAuthorize("hasRole('CLIENT')") @PostMapping("/accounts/{accountId}/deposit/{amount}") - public ResponseEntity deposit(@PathVariable int accountId, @PathVariable double amount) { - String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount; - return restTemplate.postForEntity(url, null, String.class); + public ResponseEntity deposit(@PathVariable int accountId, @PathVariable double amount) { + try { + String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount; + return restTemplate.postForEntity(url, null, String.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error during deposit: " + e.getMessage()); + } } - // 6.2 - Снятие @PreAuthorize("hasRole('CLIENT')") @PostMapping("/accounts/{accountId}/withdraw/{amount}") - public ResponseEntity withdraw(@PathVariable int accountId, @PathVariable double amount) { - String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount; - return restTemplate.postForEntity(url, null, String.class); + public ResponseEntity withdraw(@PathVariable int accountId, @PathVariable double amount) { + try { + String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount; + return restTemplate.postForEntity(url, null, String.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error during withdrawal: " + e.getMessage()); + } } - // 6.3 - Перевод @PreAuthorize("hasRole('CLIENT')") @PostMapping("/accounts/transfer/{fromId}/{toId}/{amount}") - public ResponseEntity transfer( + public ResponseEntity transfer( @PathVariable int fromId, @PathVariable int toId, @PathVariable double amount ) { - String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount; - return restTemplate.postForEntity(url, null, String.class); + try { + String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount; + return restTemplate.postForEntity(url, null, String.class); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error during transfer: " + e.getMessage()); + } } - // 7 - Деавторизация @PreAuthorize("isAuthenticated()") @PostMapping("/logout") - public ResponseEntity logout() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String username = authentication.getName(); - return ResponseEntity.ok("Client " + username + " logged out."); + public ResponseEntity logout() { + try { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = authentication.getName(); + return ResponseEntity.ok("Client " + username + " logged out."); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(500).body("Error during logout: " + e.getMessage()); + } } -} \ No newline at end of file +} From e2bf9c67eb1587eb69d91933275d9c291c037d1e Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 01:42:44 +0300 Subject: [PATCH 06/13] feat: add http headers logic; improve requests --- .../src/main/java/Services/UserService.java | 1 + .../src/main/java/Configs/SecurityConfig.java | 15 ++++---- .../java/Controllers/AdminController.java | 26 +++++++++++--- .../main/java/Controllers/AuthController.java | 36 +++++++++++++------ .../java/Controllers/ClientController.java | 35 ++++++++++++------ .../src/main/java/JWT/JwtUtil.java | 14 +++++--- .../src/main/java/Requests/LoginRequest.java | 24 +++++++++++++ .../src/main/java/Requests/UserRequest.java | 21 ++++------- .../src/main/resources/application.yml | 2 +- 9 files changed, 121 insertions(+), 53 deletions(-) create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java index 254d09f..3aea5e1 100644 --- a/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java @@ -19,6 +19,7 @@ public UserService(IUserRepository repository, PasswordEncoder passwordEncoder) } public User CreateUser(String username, String password, Role role) { + System.out.println("Creating user: " + username + " with role: " + role); String pwHash = encoder.encode(password); User user = new User(username, pwHash, role); return userRepository.save(user); diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java index 3b2aa1b..3166817 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java @@ -9,6 +9,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -32,15 +33,15 @@ public SecurityConfig(JwtAuthFilter jwtAuthFilter, CustomUserDetailService userD @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .csrf(csrf -> csrf.disable()) + .csrf(AbstractHttpConfigurer::disable) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth - .requestMatchers("/login", "/auth/**").permitAll() - .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN") - .requestMatchers("/client/**").hasAuthority("ROLE_CLIENT") - .anyRequest().authenticated() + .requestMatchers("/login").permitAll() // Путь /login доступен всем + .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN") // Путь для администраторов + .requestMatchers("/client/**").hasAuthority("ROLE_CLIENT") // Путь для клиентов + .anyRequest().authenticated() // Для остальных путей нужна авторизация ) - .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); // Добавляем фильтр JWT return http.build(); } @@ -55,7 +56,7 @@ public DaoAuthenticationProvider daoAuthenticationProvider() { @Bean public AuthenticationManager authenticationManager(DaoAuthenticationProvider daoAuthProvider) { - return new ProviderManager(List.of(daoAuthProvider)); + return new ProviderManager(List.of(daoAuthenticationProvider())); // Правильная настройка для провайдера аутентификации } @Bean diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java index ad341b0..ae82fa0 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java @@ -28,6 +28,21 @@ public AdminController(RestTemplate restTemplate, UserService userService) { this.userService = userService; } + private String getCurrentToken() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication.getCredentials() instanceof String token) { + return token; + } + return null; + } + + private HttpEntity withAuthHeaders() { + String token = getCurrentToken(); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + token); + return new HttpEntity<>(headers); + } + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/users/create") public ResponseEntity CreateUser(@RequestBody UserRequest userRequest) { @@ -51,7 +66,7 @@ public ResponseEntity GetAllUsersFiltered( "&hairColor=" + (hairColor != null ? hairColor : ""); ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} + url, HttpMethod.GET, withAuthHeaders(), new ParameterizedTypeReference<>() {} ); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { @@ -65,7 +80,7 @@ public ResponseEntity GetAllUsersFiltered( public ResponseEntity GetUserById(@PathVariable int id) { try { String url = "http://localhost:8080/users/" + id; - return restTemplate.getForEntity(url, UserDTO.class); + return restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), UserDTO.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error retrieving user: " + e.getMessage()); @@ -78,7 +93,7 @@ public ResponseEntity GetAllBankAccounts() { try { String url = "http://localhost:8080/data/accounts"; ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} + url, HttpMethod.GET, withAuthHeaders(), new ParameterizedTypeReference<>() {} ); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { @@ -92,7 +107,8 @@ public ResponseEntity GetAllBankAccounts() { public ResponseEntity GetAccountsByUserId(@PathVariable int userId) { try { String url = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); + ResponseEntity response = restTemplate.exchange( + url, HttpMethod.GET, withAuthHeaders(), BankAccountDTO[].class); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { e.printStackTrace(); @@ -113,7 +129,7 @@ public ResponseEntity getOperationsByAccountId( } ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {} + url, HttpMethod.GET, withAuthHeaders(), new ParameterizedTypeReference<>() {} ); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java index 427f3b8..c827b67 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java @@ -1,11 +1,13 @@ package Controllers; import JWT.JwtUtil; -import Requests.UserRequest; +import Requests.LoginRequest; +import Responses.JwtResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; @@ -26,26 +28,38 @@ public AuthController(AuthenticationManager authenticationManager, JwtUtil jwtUt this.userDetailsService = userDetailsService; } - @GetMapping("/login") public ResponseEntity getLoginInfo() { return ResponseEntity.status(HttpStatus.OK).body("Please send a POST request to /login with your credentials."); } - @PostMapping("/login") - public ResponseEntity login(@RequestBody UserRequest request) { + public ResponseEntity login(@RequestBody LoginRequest loginRequest) { try { - Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()) + // Проверка данных + if (loginRequest.getUsername() == null || loginRequest.getPassword() == null) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JwtResponse("Username and password are required")); + } + + // Аутентификация пользователя + Authentication auth = new UsernamePasswordAuthenticationToken( + loginRequest.getUsername(), loginRequest.getPassword() ); - UserDetails userDetails = userDetailsService.loadUserByUsername(request.getUsername()); - String token = jwtUtil.generateToken(userDetails); + authenticationManager.authenticate(auth); // Попытка аутентификации + + // Загрузка данных пользователя + UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername()); + String jwt = jwtUtil.generateToken(userDetails); // Генерация токена - return ResponseEntity.ok(token); + // Возвращаем токен в ответе + return ResponseEntity.ok(new JwtResponse(jwt)); + } catch (BadCredentialsException e) { + // Неверные учетные данные + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new JwtResponse("Invalid username or password")); } catch (Exception e) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password"); + // Любые другие исключения + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new JwtResponse("An unexpected error occurred")); } } -} \ No newline at end of file +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java index 24f7649..74758b8 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java @@ -32,13 +32,28 @@ private Long getCurrentUserId() { return userService.FindUserByUsername(username).getId(); } + private String getCurrentToken() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication.getCredentials() instanceof String token) { + return token; + } + return null; + } + + private HttpEntity withAuthHeaders() { + String token = getCurrentToken(); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + token); + return new HttpEntity<>(headers); + } + @PreAuthorize("hasRole('CLIENT')") @GetMapping("/me") public ResponseEntity getCurrentUser() { try { Long userId = getCurrentUserId(); String url = "http://localhost:8080/users/" + userId; - return restTemplate.getForEntity(url, UserDTO.class); + return restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), UserDTO.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error retrieving current user: " + e.getMessage()); @@ -51,7 +66,7 @@ public ResponseEntity getMyAccounts() { try { Long userId = getCurrentUserId(); String url = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity response = restTemplate.getForEntity(url, BankAccountDTO[].class); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), BankAccountDTO[].class); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { e.printStackTrace(); @@ -64,9 +79,9 @@ public ResponseEntity getMyAccounts() { public ResponseEntity getAccountById(@PathVariable int id) { try { Long userId = getCurrentUserId(); - String userAccountsUrl = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity accountsResponse = restTemplate.getForEntity(userAccountsUrl, BankAccountDTO[].class); + ResponseEntity accountsResponse = restTemplate.exchange( + userAccountsUrl, HttpMethod.GET, withAuthHeaders(), BankAccountDTO[].class); boolean ownsAccount = false; if (accountsResponse.getStatusCode().is2xxSuccessful()) { @@ -83,7 +98,7 @@ public ResponseEntity getAccountById(@PathVariable int id) { } String url = "http://localhost:8080/data/accounts/" + id; - return restTemplate.getForEntity(url, BankAccountDTO.class); + return restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), BankAccountDTO.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error retrieving account: " + e.getMessage()); @@ -96,7 +111,7 @@ public ResponseEntity addFriend(@PathVariable int otherId) { try { Long userId = getCurrentUserId(); String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId; - return restTemplate.postForEntity(url, null, String.class); + return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error adding friend: " + e.getMessage()); @@ -109,7 +124,7 @@ public ResponseEntity removeFriend(@PathVariable int otherId) { try { Long userId = getCurrentUserId(); String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId; - return restTemplate.postForEntity(url, null, String.class); + return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error removing friend: " + e.getMessage()); @@ -121,7 +136,7 @@ public ResponseEntity removeFriend(@PathVariable int otherId) { public ResponseEntity deposit(@PathVariable int accountId, @PathVariable double amount) { try { String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount; - return restTemplate.postForEntity(url, null, String.class); + return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error during deposit: " + e.getMessage()); @@ -133,7 +148,7 @@ public ResponseEntity deposit(@PathVariable int accountId, @PathVariable doub public ResponseEntity withdraw(@PathVariable int accountId, @PathVariable double amount) { try { String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount; - return restTemplate.postForEntity(url, null, String.class); + return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error during withdrawal: " + e.getMessage()); @@ -149,7 +164,7 @@ public ResponseEntity transfer( ) { try { String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount; - return restTemplate.postForEntity(url, null, String.class); + return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error during transfer: " + e.getMessage()); diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java index 9cefce7..5c9b0df 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java @@ -39,12 +39,13 @@ public T extractClaim(String token, Function claimsResolver) { return claimsResolver.apply(claims); } - private Claims extractAllClaims(String token) { - return Jwts.parser() - .setSigningKey(getSecretKey()) + private Claims extractAllClaims(String token){ + return Jwts + .parser() + .verifyWith(getSecretKey()) .build() - .parseClaimsJws(token) - .getBody(); + .parseSignedClaims(token) + .getPayload(); } private Boolean isTokenExpired(String token) { @@ -54,9 +55,11 @@ private Boolean isTokenExpired(String token) { public String generateToken(UserDetails userDetails) { Map claims = new HashMap<>(); + // Добавляем роли пользователя в токен claims.put("role", userDetails.getAuthorities().stream() .map(grantedAuthority -> grantedAuthority.getAuthority()) .collect(Collectors.toList())); + return createToken(claims, userDetails.getUsername()); } @@ -76,6 +79,7 @@ public Boolean validateToken(String token, UserDetails userDetails) { } public List extractRoles(String token) { + // Извлекаем роли как список строк return extractClaim(token, claims -> claims.get("role", List.class)); } } diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java new file mode 100644 index 0000000..e566517 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java @@ -0,0 +1,24 @@ +package Requests; + +public class LoginRequest { + private String username; + private String password; + + public LoginRequest() {} // ОБЯЗАТЕЛЬНО + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java index 5c44008..db2994f 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java @@ -1,20 +1,13 @@ package Requests; -import lombok.AllArgsConstructor; -import lombok.Getter; import Models.Enums.Role; +import lombok.Data; +import lombok.NoArgsConstructor; -@Getter -@AllArgsConstructor +@Data +@NoArgsConstructor public class UserRequest { - - private final String username; - private final String password; - private final Role role; - - public UserRequest(String username, String password) { - this.username = username; - this.password = password; - this.role = null; - } + private String username; + private String password; + private Role role; } diff --git a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml index 93679db..2d5e030 100644 --- a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml +++ b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml @@ -29,7 +29,7 @@ spring: # JWT Configuration jwt: - secret: my_super_secret_key_123456 + secret: Z2FsdGZpZDpxNDqf9r1mPZYuZAsZZEwOovUEY9NhcY46uo3bB9XzRXMKbK6R03Qp expiration: 86400000 # 1 day server: From 8f01c9c8fdeae03be7c1d2ca5eae224c7ac53757 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 01:59:02 +0300 Subject: [PATCH 07/13] fix: changed port --- .../PresentationApiGateway/src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml index 2d5e030..22e8c38 100644 --- a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml +++ b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml @@ -33,6 +33,7 @@ jwt: expiration: 86400000 # 1 day server: + port: 8081 error: include-message: always include-binding-errors: always From 8edb21b7a39107835561ab50978641ada25f2698 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 02:07:45 +0300 Subject: [PATCH 08/13] chore: remove unused comments --- .../src/main/java/Configs/SecurityConfig.java | 10 +++++----- .../src/main/java/Controllers/AuthController.java | 15 ++++----------- .../src/main/java/JWT/JwtUtil.java | 3 --- .../src/main/java/Requests/LoginRequest.java | 2 +- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java index 3166817..77aafab 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java @@ -36,10 +36,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .csrf(AbstractHttpConfigurer::disable) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth - .requestMatchers("/login").permitAll() // Путь /login доступен всем - .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN") // Путь для администраторов - .requestMatchers("/client/**").hasAuthority("ROLE_CLIENT") // Путь для клиентов - .anyRequest().authenticated() // Для остальных путей нужна авторизация + .requestMatchers("/login").permitAll() + .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN") + .requestMatchers("/client/**").hasAuthority("ROLE_CLIENT") + .anyRequest().authenticated() ) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); // Добавляем фильтр JWT @@ -56,7 +56,7 @@ public DaoAuthenticationProvider daoAuthenticationProvider() { @Bean public AuthenticationManager authenticationManager(DaoAuthenticationProvider daoAuthProvider) { - return new ProviderManager(List.of(daoAuthenticationProvider())); // Правильная настройка для провайдера аутентификации + return new ProviderManager(List.of(daoAuthenticationProvider())); } @Bean diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java index c827b67..1da9b2c 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java @@ -36,29 +36,22 @@ public ResponseEntity getLoginInfo() { @PostMapping("/login") public ResponseEntity login(@RequestBody LoginRequest loginRequest) { try { - // Проверка данных if (loginRequest.getUsername() == null || loginRequest.getPassword() == null) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JwtResponse("Username and password are required")); } - - // Аутентификация пользователя + Authentication auth = new UsernamePasswordAuthenticationToken( loginRequest.getUsername(), loginRequest.getPassword() ); - authenticationManager.authenticate(auth); // Попытка аутентификации - - // Загрузка данных пользователя + authenticationManager.authenticate(auth); + UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername()); - String jwt = jwtUtil.generateToken(userDetails); // Генерация токена - - // Возвращаем токен в ответе + String jwt = jwtUtil.generateToken(userDetails); return ResponseEntity.ok(new JwtResponse(jwt)); } catch (BadCredentialsException e) { - // Неверные учетные данные return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new JwtResponse("Invalid username or password")); } catch (Exception e) { - // Любые другие исключения return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new JwtResponse("An unexpected error occurred")); } } diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java index 5c9b0df..dcc0c2b 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java @@ -54,8 +54,6 @@ private Boolean isTokenExpired(String token) { public String generateToken(UserDetails userDetails) { Map claims = new HashMap<>(); - - // Добавляем роли пользователя в токен claims.put("role", userDetails.getAuthorities().stream() .map(grantedAuthority -> grantedAuthority.getAuthority()) .collect(Collectors.toList())); @@ -79,7 +77,6 @@ public Boolean validateToken(String token, UserDetails userDetails) { } public List extractRoles(String token) { - // Извлекаем роли как список строк return extractClaim(token, claims -> claims.get("role", List.class)); } } diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java index e566517..2c4651b 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java @@ -4,7 +4,7 @@ public class LoginRequest { private String username; private String password; - public LoginRequest() {} // ОБЯЗАТЕЛЬНО + public LoginRequest() {} public String getUsername() { return username; From 57aeacf8cbcdc937cdec753131765cad1963abee Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 02:24:20 +0300 Subject: [PATCH 09/13] fix: modify pom.xml on ApiGateway for CI/CD build --- ApiGateway/PresentationApiGateway/pom.xml | 26 +++++++++++++++++++++++ ApiGateway/pom.xml | 21 ------------------ 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/ApiGateway/PresentationApiGateway/pom.xml b/ApiGateway/PresentationApiGateway/pom.xml index de82c16..0276eb5 100644 --- a/ApiGateway/PresentationApiGateway/pom.xml +++ b/ApiGateway/PresentationApiGateway/pom.xml @@ -18,11 +18,37 @@ + + com.example DataAccessApiGateway 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + Console.ApiGatewayApp + + + + + \ No newline at end of file diff --git a/ApiGateway/pom.xml b/ApiGateway/pom.xml index 64a2731..b64166c 100644 --- a/ApiGateway/pom.xml +++ b/ApiGateway/pom.xml @@ -38,10 +38,6 @@ - - org.springframework.boot - spring-boot-starter - @@ -91,21 +87,4 @@ - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - - \ No newline at end of file From 66d27370471e98a9547a1c75d5c78895fbfb8946 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 13:46:49 +0300 Subject: [PATCH 10/13] chore: naming --- .../{IUserRepository.java => UserRepository.java} | 2 +- .../src/main/java/Services/CustomUserDetailService.java | 6 +++--- .../src/main/java/Services/UserService.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename ApiGateway/DataAccessApiGateway/src/main/java/Repositories/{IUserRepository.java => UserRepository.java} (78%) diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/IUserRepository.java b/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/UserRepository.java similarity index 78% rename from ApiGateway/DataAccessApiGateway/src/main/java/Repositories/IUserRepository.java rename to ApiGateway/DataAccessApiGateway/src/main/java/Repositories/UserRepository.java index b40f39c..a00bcc1 100644 --- a/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/IUserRepository.java +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/UserRepository.java @@ -7,6 +7,6 @@ import java.util.Optional; @Repository -public interface IUserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { Optional findByUsername(String username); } diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java index adbb5f9..71574be 100644 --- a/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java @@ -1,6 +1,6 @@ package Services; -import Repositories.IUserRepository; +import Repositories.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -10,10 +10,10 @@ @Service public class CustomUserDetailService implements UserDetailsService { - private final IUserRepository userRepository; + private final UserRepository userRepository; @Autowired - public CustomUserDetailService(IUserRepository repository) { + public CustomUserDetailService(UserRepository repository) { this.userRepository = repository; } diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java index 3aea5e1..f4a9955 100644 --- a/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java @@ -2,18 +2,18 @@ import Models.Entities.User; import Models.Enums.Role; -import Repositories.IUserRepository; +import Repositories.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service public class UserService { - private final IUserRepository userRepository; + private final UserRepository userRepository; private final PasswordEncoder encoder; @Autowired - public UserService(IUserRepository repository, PasswordEncoder passwordEncoder) { + public UserService(UserRepository repository, PasswordEncoder passwordEncoder) { this.userRepository = repository; this.encoder = passwordEncoder; } From ac98d03c960d744d559971b10428adb08c163343 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 14:00:15 +0300 Subject: [PATCH 11/13] feat: move util methods to separate classes --- .../java/Controllers/AdminController.java | 30 ++++------- .../java/Controllers/ClientController.java | 54 +++++++------------ .../src/main/java/JWT/HttpAdminUtil.java | 26 +++++++++ .../src/main/java/JWT/HttpClientUtil.java | 32 +++++++++++ 4 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpAdminUtil.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpClientUtil.java diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java index ae82fa0..224a8aa 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java @@ -3,6 +3,7 @@ import DTO.BankAccountDTO; import DTO.OperationDTO; import DTO.UserDTO; +import JWT.HttpAdminUtil; import Requests.UserRequest; import Services.UserService; import org.springframework.beans.factory.annotation.Autowired; @@ -21,26 +22,13 @@ public class AdminController { private final RestTemplate restTemplate; private final UserService userService; + private final HttpAdminUtil adminUtil; @Autowired - public AdminController(RestTemplate restTemplate, UserService userService) { + public AdminController(RestTemplate restTemplate, UserService userService, HttpAdminUtil adminUtil) { this.restTemplate = restTemplate; this.userService = userService; - } - - private String getCurrentToken() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication.getCredentials() instanceof String token) { - return token; - } - return null; - } - - private HttpEntity withAuthHeaders() { - String token = getCurrentToken(); - HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + token); - return new HttpEntity<>(headers); + this.adminUtil = adminUtil; } @PreAuthorize("hasRole('ADMIN')") @@ -66,7 +54,7 @@ public ResponseEntity GetAllUsersFiltered( "&hairColor=" + (hairColor != null ? hairColor : ""); ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, withAuthHeaders(), new ParameterizedTypeReference<>() {} + url, HttpMethod.GET, adminUtil.withAuthHeaders(), new ParameterizedTypeReference<>() {} ); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { @@ -80,7 +68,7 @@ url, HttpMethod.GET, withAuthHeaders(), new ParameterizedTypeReference<>() {} public ResponseEntity GetUserById(@PathVariable int id) { try { String url = "http://localhost:8080/users/" + id; - return restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), UserDTO.class); + return restTemplate.exchange(url, HttpMethod.GET, adminUtil.withAuthHeaders(), UserDTO.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error retrieving user: " + e.getMessage()); @@ -93,7 +81,7 @@ public ResponseEntity GetAllBankAccounts() { try { String url = "http://localhost:8080/data/accounts"; ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, withAuthHeaders(), new ParameterizedTypeReference<>() {} + url, HttpMethod.GET, adminUtil.withAuthHeaders(), new ParameterizedTypeReference<>() {} ); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { @@ -108,7 +96,7 @@ public ResponseEntity GetAccountsByUserId(@PathVariable int userId) { try { String url = "http://localhost:8080/data/users/" + userId + "/accounts"; ResponseEntity response = restTemplate.exchange( - url, HttpMethod.GET, withAuthHeaders(), BankAccountDTO[].class); + url, HttpMethod.GET, adminUtil.withAuthHeaders(), BankAccountDTO[].class); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { e.printStackTrace(); @@ -129,7 +117,7 @@ public ResponseEntity getOperationsByAccountId( } ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, withAuthHeaders(), new ParameterizedTypeReference<>() {} + url, HttpMethod.GET, adminUtil.withAuthHeaders(), new ParameterizedTypeReference<>() {} ); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java index 74758b8..12bafd2 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java @@ -2,6 +2,7 @@ import DTO.BankAccountDTO; import DTO.UserDTO; +import JWT.HttpClientUtil; import Services.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.*; @@ -19,41 +20,22 @@ public class ClientController { private final RestTemplate restTemplate; private final UserService userService; + private final HttpClientUtil clientUtil; @Autowired - public ClientController(RestTemplate restTemplate, UserService userService) { + public ClientController(RestTemplate restTemplate, UserService userService, HttpClientUtil clientUtil) { this.restTemplate = restTemplate; this.userService = userService; - } - - private Long getCurrentUserId() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String username = authentication.getName(); - return userService.FindUserByUsername(username).getId(); - } - - private String getCurrentToken() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication.getCredentials() instanceof String token) { - return token; - } - return null; - } - - private HttpEntity withAuthHeaders() { - String token = getCurrentToken(); - HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + token); - return new HttpEntity<>(headers); + this.clientUtil = clientUtil; } @PreAuthorize("hasRole('CLIENT')") @GetMapping("/me") public ResponseEntity getCurrentUser() { try { - Long userId = getCurrentUserId(); + Long userId = clientUtil.getCurrentUserId(userService); String url = "http://localhost:8080/users/" + userId; - return restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), UserDTO.class); + return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), UserDTO.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error retrieving current user: " + e.getMessage()); @@ -64,9 +46,9 @@ public ResponseEntity getCurrentUser() { @GetMapping("/accounts") public ResponseEntity getMyAccounts() { try { - Long userId = getCurrentUserId(); + Long userId = clientUtil.getCurrentUserId(userService); String url = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), BankAccountDTO[].class); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class); return ResponseEntity.ok(response.getBody()); } catch (Exception e) { e.printStackTrace(); @@ -78,10 +60,10 @@ public ResponseEntity getMyAccounts() { @GetMapping("/accounts/{id}") public ResponseEntity getAccountById(@PathVariable int id) { try { - Long userId = getCurrentUserId(); + Long userId = clientUtil.getCurrentUserId(userService); String userAccountsUrl = "http://localhost:8080/data/users/" + userId + "/accounts"; ResponseEntity accountsResponse = restTemplate.exchange( - userAccountsUrl, HttpMethod.GET, withAuthHeaders(), BankAccountDTO[].class); + userAccountsUrl, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class); boolean ownsAccount = false; if (accountsResponse.getStatusCode().is2xxSuccessful()) { @@ -98,7 +80,7 @@ public ResponseEntity getAccountById(@PathVariable int id) { } String url = "http://localhost:8080/data/accounts/" + id; - return restTemplate.exchange(url, HttpMethod.GET, withAuthHeaders(), BankAccountDTO.class); + return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error retrieving account: " + e.getMessage()); @@ -109,9 +91,9 @@ public ResponseEntity getAccountById(@PathVariable int id) { @PostMapping("/friends/add/{otherId}") public ResponseEntity addFriend(@PathVariable int otherId) { try { - Long userId = getCurrentUserId(); + Long userId = clientUtil.getCurrentUserId(userService); String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId; - return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error adding friend: " + e.getMessage()); @@ -122,9 +104,9 @@ public ResponseEntity addFriend(@PathVariable int otherId) { @PostMapping("/friends/remove/{otherId}") public ResponseEntity removeFriend(@PathVariable int otherId) { try { - Long userId = getCurrentUserId(); + Long userId = clientUtil.getCurrentUserId(userService); String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId; - return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error removing friend: " + e.getMessage()); @@ -136,7 +118,7 @@ public ResponseEntity removeFriend(@PathVariable int otherId) { public ResponseEntity deposit(@PathVariable int accountId, @PathVariable double amount) { try { String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount; - return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error during deposit: " + e.getMessage()); @@ -148,7 +130,7 @@ public ResponseEntity deposit(@PathVariable int accountId, @PathVariable doub public ResponseEntity withdraw(@PathVariable int accountId, @PathVariable double amount) { try { String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount; - return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error during withdrawal: " + e.getMessage()); @@ -164,7 +146,7 @@ public ResponseEntity transfer( ) { try { String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount; - return restTemplate.exchange(url, HttpMethod.POST, withAuthHeaders(), String.class); + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("Error during transfer: " + e.getMessage()); diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpAdminUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpAdminUtil.java new file mode 100644 index 0000000..65f829c --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpAdminUtil.java @@ -0,0 +1,26 @@ +package JWT; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Service +public class HttpAdminUtil { + private String getCurrentToken() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication.getCredentials() instanceof String token) { + return token; + } + return null; + } + + public HttpEntity withAuthHeaders() { + String token = getCurrentToken(); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + token); + return new HttpEntity<>(headers); + } +} + diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpClientUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpClientUtil.java new file mode 100644 index 0000000..1fc2292 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpClientUtil.java @@ -0,0 +1,32 @@ +package JWT; + +import Services.UserService; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Service +public class HttpClientUtil { + private String getCurrentToken() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication.getCredentials() instanceof String token) { + return token; + } + return null; + } + + public Long getCurrentUserId(UserService userService) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = authentication.getName(); + return userService.FindUserByUsername(username).getId(); + } + + public HttpEntity withAuthHeaders() { + String token = getCurrentToken(); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + token); + return new HttpEntity<>(headers); + } +} From 8d3af51b061a6904a6680a900407e3aaf0f85a84 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 14:31:57 +0300 Subject: [PATCH 12/13] feat: implement ClientService, AdminService and GlobalExceptionHandler --- ApiGateway/DataAccessApiGateway/pom.xml | 5 + .../src/main/java/Services/AdminService.java | 92 ++++++++++++++ .../src/main/java/Services/ClientService.java | 89 +++++++++++++ .../java/Controllers/AdminController.java | 93 ++++---------- .../java/Controllers/ClientController.java | 118 +++--------------- .../Handlers/GlobalExceptionHandler.java | 18 +++ .../main/java/Requests/UserFilterRequest.java | 9 ++ 7 files changed, 249 insertions(+), 175 deletions(-) create mode 100644 ApiGateway/DataAccessApiGateway/src/main/java/Services/AdminService.java create mode 100644 ApiGateway/DataAccessApiGateway/src/main/java/Services/ClientService.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Controllers/Handlers/GlobalExceptionHandler.java create mode 100644 ApiGateway/PresentationApiGateway/src/main/java/Requests/UserFilterRequest.java diff --git a/ApiGateway/DataAccessApiGateway/pom.xml b/ApiGateway/DataAccessApiGateway/pom.xml index 593fa2b..e0ec9db 100644 --- a/ApiGateway/DataAccessApiGateway/pom.xml +++ b/ApiGateway/DataAccessApiGateway/pom.xml @@ -23,6 +23,11 @@ ApplicationApiGateway 1.0-SNAPSHOT + + com.example + PresentationApiGateway + 1.0-SNAPSHOT + \ No newline at end of file diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/AdminService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/AdminService.java new file mode 100644 index 0000000..85a15ae --- /dev/null +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/AdminService.java @@ -0,0 +1,92 @@ +package Services; + +import Controllers.Handlers.GlobalExceptionHandler; +import DTO.BankAccountDTO; +import DTO.OperationDTO; +import DTO.UserDTO; +import JWT.HttpAdminUtil; +import Requests.UserFilterRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.List; + +@Service +public class AdminService { + + private final RestTemplate restTemplate; + private final HttpAdminUtil adminUtil; + + @Autowired + public AdminService(RestTemplate restTemplate, HttpAdminUtil adminUtil) { + this.restTemplate = restTemplate; + this.adminUtil = adminUtil; + } + + public List getFilteredUsers(UserFilterRequest filterRequest) { + String url = "http://localhost:8080/data/users?sex=" + + (filterRequest.getSex() != null ? filterRequest.getSex() : "") + + "&hairColor=" + (filterRequest.getHairColor() != null ? filterRequest.getHairColor() : ""); + + ResponseEntity> response = restTemplate.exchange( + url, + HttpMethod.GET, + adminUtil.withAuthHeaders(), + new ParameterizedTypeReference<>() {} + ); + return response.getBody(); + } + + public UserDTO getUserById(int id) { + String url = "http://localhost:8080/users/" + id; + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + adminUtil.withAuthHeaders(), + UserDTO.class + ); + return response.getBody(); + } + + public List getAllBankAccounts() { + String url = "http://localhost:8080/data/accounts"; + ResponseEntity> response = restTemplate.exchange( + url, + HttpMethod.GET, + adminUtil.withAuthHeaders(), + new ParameterizedTypeReference<>() {} + ); + return response.getBody(); + } + + public List getAccountsByUserId(int userId) { + String url = "http://localhost:8080/data/users/" + userId + "/accounts"; + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + adminUtil.withAuthHeaders(), + BankAccountDTO[].class + ); + return Arrays.asList(response.getBody()); + } + + public List getOperationsByAccountId(int accountId, String type) { + String url = "http://localhost:8080/data/operations?accountId=" + accountId; + if (type != null && !type.isEmpty()) { + url += "&type=" + type; + } + + ResponseEntity> response = restTemplate.exchange( + url, + HttpMethod.GET, + adminUtil.withAuthHeaders(), + new ParameterizedTypeReference<>() {} + ); + return response.getBody(); + } +} diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/ClientService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/ClientService.java new file mode 100644 index 0000000..2872e98 --- /dev/null +++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/ClientService.java @@ -0,0 +1,89 @@ +package Services; + +import DTO.BankAccountDTO; +import DTO.UserDTO; +import JWT.HttpClientUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.Objects; + +@Service +public class ClientService { + + private final RestTemplate restTemplate; + private final HttpClientUtil clientUtil; + private final UserService userService; + + @Autowired + public ClientService(RestTemplate restTemplate, HttpClientUtil clientUtil, UserService userService) { + this.restTemplate = restTemplate; + this.clientUtil = clientUtil; + this.userService = userService; + } + + public ResponseEntity getCurrentUser() { + Long userId = clientUtil.getCurrentUserId(userService); + String url = "http://localhost:8080/users/" + userId; + return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), UserDTO.class); + } + + public ResponseEntity getMyAccounts() { + Long userId = clientUtil.getCurrentUserId(userService); + String url = "http://localhost:8080/data/users/" + userId + "/accounts"; + return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class); + } + + public ResponseEntity getAccountById(int id) { + Long userId = clientUtil.getCurrentUserId(userService); + String userAccountsUrl = "http://localhost:8080/data/users/" + userId + "/accounts"; + ResponseEntity accountsResponse = restTemplate.exchange( + userAccountsUrl, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class); + + boolean ownsAccount = false; + if (accountsResponse.getStatusCode().is2xxSuccessful()) { + for (BankAccountDTO account : Objects.requireNonNull(accountsResponse.getBody())) { + if (account.getId() == id) { + ownsAccount = true; + break; + } + } + } + + if (!ownsAccount) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Access denied to account."); + } + + String url = "http://localhost:8080/data/accounts/" + id; + return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO.class); + } + + public ResponseEntity addFriend(int otherId) { + Long userId = clientUtil.getCurrentUserId(userService); + String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId; + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); + } + + public ResponseEntity removeFriend(int otherId) { + Long userId = clientUtil.getCurrentUserId(userService); + String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId; + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); + } + + public ResponseEntity deposit(int accountId, double amount) { + String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount; + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); + } + + public ResponseEntity withdraw(int accountId, double amount) { + String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount; + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); + } + + public ResponseEntity transfer(int fromId, int toId, double amount) { + String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount; + return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java index 224a8aa..ab1931f 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java @@ -3,129 +3,78 @@ import DTO.BankAccountDTO; import DTO.OperationDTO; import DTO.UserDTO; -import JWT.HttpAdminUtil; +import Requests.UserFilterRequest; import Requests.UserRequest; +import Services.AdminService; import Services.UserService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.*; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; import java.util.List; @RestController @RequestMapping("/admin") public class AdminController { - private final RestTemplate restTemplate; private final UserService userService; - private final HttpAdminUtil adminUtil; + private final AdminService adminService; @Autowired - public AdminController(RestTemplate restTemplate, UserService userService, HttpAdminUtil adminUtil) { - this.restTemplate = restTemplate; + public AdminController(UserService userService, AdminService adminService) { this.userService = userService; - this.adminUtil = adminUtil; + this.adminService = adminService; } @PreAuthorize("hasRole('ADMIN')") @PostMapping("/users/create") public ResponseEntity CreateUser(@RequestBody UserRequest userRequest) { - try { - userService.CreateUser(userRequest.getUsername(), userRequest.getPassword(), userRequest.getRole()); - return ResponseEntity.ok("User created successfully."); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error creating user: " + e.getMessage()); - } + userService.CreateUser(userRequest.getUsername(), userRequest.getPassword(), userRequest.getRole()); + return ResponseEntity.ok("User created successfully."); } @PreAuthorize("hasRole('ADMIN')") @GetMapping("/users") - public ResponseEntity GetAllUsersFiltered( + public ResponseEntity> getAllUsersFiltered( @RequestParam(required = false) String sex, @RequestParam(required = false) String hairColor ) { - try { - String url = "http://localhost:8080/data/users?sex=" + (sex != null ? sex : "") + - "&hairColor=" + (hairColor != null ? hairColor : ""); - - ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, adminUtil.withAuthHeaders(), new ParameterizedTypeReference<>() {} - ); - return ResponseEntity.ok(response.getBody()); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving users: " + e.getMessage()); - } + UserFilterRequest request = new UserFilterRequest(); + request.setSex(sex); + request.setHairColor(hairColor); + return ResponseEntity.ok(adminService.getFilteredUsers(request)); } @PreAuthorize("hasRole('ADMIN')") @GetMapping("/users/{id}") - public ResponseEntity GetUserById(@PathVariable int id) { - try { - String url = "http://localhost:8080/users/" + id; - return restTemplate.exchange(url, HttpMethod.GET, adminUtil.withAuthHeaders(), UserDTO.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving user: " + e.getMessage()); - } + public ResponseEntity getUserById(@PathVariable int id) { + return ResponseEntity.ok(adminService.getUserById(id)); } @PreAuthorize("hasRole('ADMIN')") @GetMapping("/bankaccounts") - public ResponseEntity GetAllBankAccounts() { - try { - String url = "http://localhost:8080/data/accounts"; - ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, adminUtil.withAuthHeaders(), new ParameterizedTypeReference<>() {} - ); - return ResponseEntity.ok(response.getBody()); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving accounts: " + e.getMessage()); - } + public ResponseEntity> getAllBankAccounts() { + return ResponseEntity.ok(adminService.getAllBankAccounts()); } @PreAuthorize("hasRole('ADMIN')") @GetMapping("/bankaccounts/user/{userId}") - public ResponseEntity GetAccountsByUserId(@PathVariable int userId) { - try { - String url = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity response = restTemplate.exchange( - url, HttpMethod.GET, adminUtil.withAuthHeaders(), BankAccountDTO[].class); - return ResponseEntity.ok(response.getBody()); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving accounts for user: " + e.getMessage()); - } + public ResponseEntity> getAccountsByUserId(@PathVariable int userId) { + return ResponseEntity.ok(adminService.getAccountsByUserId(userId)); } @PreAuthorize("hasRole('ADMIN')") @GetMapping("/bankaccounts/{id}/operations") - public ResponseEntity getOperationsByAccountId( + public ResponseEntity> getOperationsByAccountId( @PathVariable int id, @RequestParam(required = false) String type ) { - try { - String url = "http://localhost:8080/data/operations?accountId=" + id; - if (type != null && !type.isEmpty()) { - url += "&type=" + type; - } - - ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, adminUtil.withAuthHeaders(), new ParameterizedTypeReference<>() {} - ); - return ResponseEntity.ok(response.getBody()); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving operations: " + e.getMessage()); - } + return ResponseEntity.ok(adminService.getOperationsByAccountId(id, type)); } + @PreAuthorize("isAuthenticated()") @PostMapping("/logout") public ResponseEntity Logout() { diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java index 12bafd2..d2f5893 100644 --- a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java @@ -1,140 +1,64 @@ package Controllers; -import DTO.BankAccountDTO; -import DTO.UserDTO; -import JWT.HttpClientUtil; -import Services.UserService; +import Services.ClientService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.*; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; - -import java.util.Objects; @RestController @RequestMapping("/client") public class ClientController { - private final RestTemplate restTemplate; - private final UserService userService; - private final HttpClientUtil clientUtil; + private final ClientService clientService; @Autowired - public ClientController(RestTemplate restTemplate, UserService userService, HttpClientUtil clientUtil) { - this.restTemplate = restTemplate; - this.userService = userService; - this.clientUtil = clientUtil; + public ClientController(ClientService clientService) { + this.clientService = clientService; } @PreAuthorize("hasRole('CLIENT')") @GetMapping("/me") public ResponseEntity getCurrentUser() { - try { - Long userId = clientUtil.getCurrentUserId(userService); - String url = "http://localhost:8080/users/" + userId; - return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), UserDTO.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving current user: " + e.getMessage()); - } + return clientService.getCurrentUser(); } @PreAuthorize("hasRole('CLIENT')") @GetMapping("/accounts") public ResponseEntity getMyAccounts() { - try { - Long userId = clientUtil.getCurrentUserId(userService); - String url = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class); - return ResponseEntity.ok(response.getBody()); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving accounts: " + e.getMessage()); - } + return clientService.getMyAccounts(); } @PreAuthorize("hasRole('CLIENT')") @GetMapping("/accounts/{id}") public ResponseEntity getAccountById(@PathVariable int id) { - try { - Long userId = clientUtil.getCurrentUserId(userService); - String userAccountsUrl = "http://localhost:8080/data/users/" + userId + "/accounts"; - ResponseEntity accountsResponse = restTemplate.exchange( - userAccountsUrl, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class); - - boolean ownsAccount = false; - if (accountsResponse.getStatusCode().is2xxSuccessful()) { - for (BankAccountDTO account : Objects.requireNonNull(accountsResponse.getBody())) { - if (account.getId() == id) { - ownsAccount = true; - break; - } - } - } - - if (!ownsAccount) { - return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Access denied to account."); - } - - String url = "http://localhost:8080/data/accounts/" + id; - return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error retrieving account: " + e.getMessage()); - } + return clientService.getAccountById(id); } @PreAuthorize("hasRole('CLIENT')") @PostMapping("/friends/add/{otherId}") public ResponseEntity addFriend(@PathVariable int otherId) { - try { - Long userId = clientUtil.getCurrentUserId(userService); - String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId; - return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error adding friend: " + e.getMessage()); - } + return clientService.addFriend(otherId); } @PreAuthorize("hasRole('CLIENT')") @PostMapping("/friends/remove/{otherId}") public ResponseEntity removeFriend(@PathVariable int otherId) { - try { - Long userId = clientUtil.getCurrentUserId(userService); - String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId; - return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error removing friend: " + e.getMessage()); - } + return clientService.removeFriend(otherId); } @PreAuthorize("hasRole('CLIENT')") @PostMapping("/accounts/{accountId}/deposit/{amount}") public ResponseEntity deposit(@PathVariable int accountId, @PathVariable double amount) { - try { - String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount; - return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error during deposit: " + e.getMessage()); - } + return clientService.deposit(accountId, amount); } @PreAuthorize("hasRole('CLIENT')") @PostMapping("/accounts/{accountId}/withdraw/{amount}") public ResponseEntity withdraw(@PathVariable int accountId, @PathVariable double amount) { - try { - String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount; - return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error during withdrawal: " + e.getMessage()); - } + return clientService.withdraw(accountId, amount); } @PreAuthorize("hasRole('CLIENT')") @@ -144,25 +68,13 @@ public ResponseEntity transfer( @PathVariable int toId, @PathVariable double amount ) { - try { - String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount; - return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error during transfer: " + e.getMessage()); - } + return clientService.transfer(fromId, toId, amount); } @PreAuthorize("isAuthenticated()") @PostMapping("/logout") public ResponseEntity logout() { - try { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String username = authentication.getName(); - return ResponseEntity.ok("Client " + username + " logged out."); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(500).body("Error during logout: " + e.getMessage()); - } + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return ResponseEntity.ok("Client " + authentication.getName() + " logged out."); } -} +} \ No newline at end of file diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/Handlers/GlobalExceptionHandler.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/Handlers/GlobalExceptionHandler.java new file mode 100644 index 0000000..c18c0f4 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/Handlers/GlobalExceptionHandler.java @@ -0,0 +1,18 @@ +package Controllers.Handlers; + + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + e.printStackTrace(); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("Error: " + e.getMessage()); + } +} diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserFilterRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserFilterRequest.java new file mode 100644 index 0000000..4b47a63 --- /dev/null +++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserFilterRequest.java @@ -0,0 +1,9 @@ +package Requests; + +import lombok.Data; + +@Data +public class UserFilterRequest { + private String sex; + private String hairColor; +} From b3fed040881ca044203d9c68f0be094872c3f599 Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Sat, 10 May 2025 14:36:39 +0300 Subject: [PATCH 13/13] fix: remove cycled dependency in BLL/UI --- ApiGateway/DataAccessApiGateway/pom.xml | 5 ----- .../src/main/java/Services/AdminService.java | 1 - .../src/main/java/Services/ClientService.java | 0 3 files changed, 6 deletions(-) rename ApiGateway/{DataAccessApiGateway => PresentationApiGateway}/src/main/java/Services/AdminService.java (98%) rename ApiGateway/{DataAccessApiGateway => PresentationApiGateway}/src/main/java/Services/ClientService.java (100%) diff --git a/ApiGateway/DataAccessApiGateway/pom.xml b/ApiGateway/DataAccessApiGateway/pom.xml index e0ec9db..593fa2b 100644 --- a/ApiGateway/DataAccessApiGateway/pom.xml +++ b/ApiGateway/DataAccessApiGateway/pom.xml @@ -23,11 +23,6 @@ ApplicationApiGateway 1.0-SNAPSHOT - - com.example - PresentationApiGateway - 1.0-SNAPSHOT - \ No newline at end of file diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/AdminService.java b/ApiGateway/PresentationApiGateway/src/main/java/Services/AdminService.java similarity index 98% rename from ApiGateway/DataAccessApiGateway/src/main/java/Services/AdminService.java rename to ApiGateway/PresentationApiGateway/src/main/java/Services/AdminService.java index 85a15ae..721f566 100644 --- a/ApiGateway/DataAccessApiGateway/src/main/java/Services/AdminService.java +++ b/ApiGateway/PresentationApiGateway/src/main/java/Services/AdminService.java @@ -1,6 +1,5 @@ package Services; -import Controllers.Handlers.GlobalExceptionHandler; import DTO.BankAccountDTO; import DTO.OperationDTO; import DTO.UserDTO; diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/ClientService.java b/ApiGateway/PresentationApiGateway/src/main/java/Services/ClientService.java similarity index 100% rename from ApiGateway/DataAccessApiGateway/src/main/java/Services/ClientService.java rename to ApiGateway/PresentationApiGateway/src/main/java/Services/ClientService.java