diff --git a/pom.xml b/pom.xml index 428b562..809654a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot @@ -50,10 +50,10 @@ org.springframework.boot spring-boot-starter-data-jpa - - - - + + org.springframework.boot + spring-boot-starter-security + org.springframework.boot spring-boot-starter-web @@ -62,27 +62,16 @@ org.springframework.boot spring-boot-starter-web-services - - - - - org.springframework.session spring-session-jdbc - org.springframework.boot spring-boot-devtools runtime true - - - - - com.mysql mysql-connector-j @@ -210,4 +199,4 @@ - + \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/Config/CorsConfig.java b/src/main/java/org/cmh/backend/Config/CorsConfig.java index 7852636..d178880 100644 --- a/src/main/java/org/cmh/backend/Config/CorsConfig.java +++ b/src/main/java/org/cmh/backend/Config/CorsConfig.java @@ -1,5 +1,4 @@ package org.cmh.backend.Config; -// CorsConfig.java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,13 +13,12 @@ public class CorsConfig { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins("http://localhost:8080") + registry.addMapping("/api/**") + .allowedOrigins("http://localhost:3000") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true); } }; } -} - +} \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/Config/SecurityConfig.java b/src/main/java/org/cmh/backend/Config/SecurityConfig.java new file mode 100644 index 0000000..48dd95f --- /dev/null +++ b/src/main/java/org/cmh/backend/Config/SecurityConfig.java @@ -0,0 +1,54 @@ +package org.cmh.backend.Config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import java.util.List; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .authorizeHttpRequests(authorize -> authorize + .requestMatchers("/api/auth/register", "/api/auth/login").permitAll() + .anyRequest().authenticated() + ); + + return http.build(); + } + + @Bean + public UrlBasedCorsConfigurationSource corsConfigurationSource() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.setAllowedOrigins(List.of("http://localhost:3000")); + config.setAllowedHeaders(List.of("*")); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + source.registerCorsConfiguration("/**", config); + return source; + } + + @Bean + public CorsFilter corsFilter() { + return new CorsFilter(corsConfigurationSource()); + } +} \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/authentication/controller/AuthenticationController.java b/src/main/java/org/cmh/backend/authentication/controller/AuthenticationController.java index 8ec56df..974eed0 100644 --- a/src/main/java/org/cmh/backend/authentication/controller/AuthenticationController.java +++ b/src/main/java/org/cmh/backend/authentication/controller/AuthenticationController.java @@ -1,13 +1,45 @@ package org.cmh.backend.authentication.controller; +import org.cmh.backend.authentication.model.User; +import org.cmh.backend.authentication.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; +import java.util.HashMap; +import java.util.Map; @RestController -class AuthenticationController { +@RequestMapping("/api/auth") +public class AuthenticationController { + + @Autowired + private UserService userService; + + @PostMapping("/register") + public User register(@RequestBody User user) { + return userService.register(user); + } + + @PostMapping("/login") + public ResponseEntity> login(@RequestBody User loginRequest) { + User user = userService.login(loginRequest.getUsername(), loginRequest.getPassword()); + if (user == null) { + throw new RuntimeException("Invalid username or password"); + } + Map response = new HashMap<>(); + response.put("userId", user.getId()); + response.put("user", user); + return ResponseEntity.ok(response); + } + @GetMapping("/hello") public String hello(){ return "Hello SpringBoot!"; } + + @PostMapping("/getVerificationCode") + public String getVerificationCode(@RequestBody String contact) { + return "Verification code sent to " + contact; + } } \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/authentication/controller/UserController.java b/src/main/java/org/cmh/backend/authentication/controller/UserController.java index f4e6aae..a3a38c5 100644 --- a/src/main/java/org/cmh/backend/authentication/controller/UserController.java +++ b/src/main/java/org/cmh/backend/authentication/controller/UserController.java @@ -4,20 +4,52 @@ import org.cmh.backend.authentication.model.User; import org.cmh.backend.authentication.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { + @Autowired private UserService userService; - @GetMapping("/{username}") - public ResponseEntity getUser(@PathVariable String username) { - User user = userService.getUserByUsername(username); - return ResponseEntity.ok(user); + @CrossOrigin(origins = "http://localhost:3000") + @PostMapping("/register") + public ResponseEntity register(@RequestBody User user) { + User registeredUser = userService.register(user); + return ResponseEntity.ok(registeredUser); } -} + + @PostMapping("/login") + public ResponseEntity login(@RequestParam String username, @RequestParam String password) { + User user = userService.login(username, password); + if (user != null) { + return ResponseEntity.ok(user); + } + return ResponseEntity.status(401).build(); + } + + @PutMapping("/update") + public ResponseEntity updateUserInfo(@RequestBody User user) { + User updatedUser = userService.updateUserInfo(user); + return ResponseEntity.ok(updatedUser); + } + + @PutMapping("/changePassword") + public ResponseEntity changePassword(@RequestParam Long userId, @RequestParam String oldPassword, @RequestParam String newPassword) { + boolean isChanged = userService.changePassword(userId, oldPassword, newPassword); + if (isChanged) { + return ResponseEntity.ok("Password changed successfully"); + } + return ResponseEntity.status(400).body("Old password is incorrect"); + } + + @GetMapping("/{userId}") + public ResponseEntity getUserInfo(@PathVariable Long userId) { + User user = userService.findById(userId).orElse(null); + if (user != null) { + return ResponseEntity.ok(user); + } + return ResponseEntity.status(404).build(); + } +} \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/authentication/model/User.java b/src/main/java/org/cmh/backend/authentication/model/User.java index ba9aa53..0a2900c 100644 --- a/src/main/java/org/cmh/backend/authentication/model/User.java +++ b/src/main/java/org/cmh/backend/authentication/model/User.java @@ -1,39 +1,34 @@ +// User.java package org.cmh.backend.authentication.model; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data @Entity +@Table(name = "Chester") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Column(nullable = false, unique = true) private String username; + + @Column(nullable = false) private String password; - public Long getId() { - return id; - } + @Column(nullable = false) + private String email; - public void setId(Long id) { - this.id = id; - } + @Column(nullable = false) + private String role; - public String getUsername() { - return username; - } + private String phoneNumber; + private String company; - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } + @Column(nullable = false) + private LocalDateTime createdDate; } \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/authentication/repository/UserRepository.java b/src/main/java/org/cmh/backend/authentication/repository/UserRepository.java index 2c5116e..473bba9 100644 --- a/src/main/java/org/cmh/backend/authentication/repository/UserRepository.java +++ b/src/main/java/org/cmh/backend/authentication/repository/UserRepository.java @@ -2,7 +2,13 @@ package org.cmh.backend.authentication.repository; import org.cmh.backend.authentication.model.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.Optional; + +@Repository public interface UserRepository extends JpaRepository { User findByUsername(String username); -} + Optional findById(Long id); + User save(User user); +} \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/authentication/service/AuthenticationService.java b/src/main/java/org/cmh/backend/authentication/service/AuthenticationService.java deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/org/cmh/backend/authentication/service/UserService.java b/src/main/java/org/cmh/backend/authentication/service/UserService.java index 30e5134..3e2c708 100644 --- a/src/main/java/org/cmh/backend/authentication/service/UserService.java +++ b/src/main/java/org/cmh/backend/authentication/service/UserService.java @@ -1,16 +1,14 @@ package org.cmh.backend.authentication.service; import org.cmh.backend.authentication.model.User; -import org.cmh.backend.authentication.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -@Service -public class UserService { - @Autowired - private UserRepository userRepository; +import java.util.Optional; - public User getUserByUsername(String username) { - return userRepository.findByUsername(username); - } -} +// UserService.java +public interface UserService { + User register(User user); + User login(String username, String password); + User updateUserInfo(User user); + boolean changePassword(Long userId, String oldPassword, String newPassword); + Optional findById(Long id); +} \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/authentication/service/UserServiceImpl.java b/src/main/java/org/cmh/backend/authentication/service/UserServiceImpl.java new file mode 100644 index 0000000..ae8be9b --- /dev/null +++ b/src/main/java/org/cmh/backend/authentication/service/UserServiceImpl.java @@ -0,0 +1,58 @@ +package org.cmh.backend.authentication.service; + +import org.cmh.backend.authentication.model.User; +import org.cmh.backend.authentication.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.Optional; + +// UserServiceImpl.java +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public User register(User user) { + user.setPassword(passwordEncoder.encode(user.getPassword())); + user.setCreatedDate(LocalDateTime.now()); + return userRepository.save(user); + } + + @Override + public User login(String username, String password) { + User user = userRepository.findByUsername(username); + if (user != null && passwordEncoder.matches(password, user.getPassword())) { + return user; + } + return null; + } + + @Override + public User updateUserInfo(User user) { + return userRepository.save(user); + } + + @Override + public boolean changePassword(Long userId, String oldPassword, String newPassword) { + User user = userRepository.findById(userId).orElse(null); + if (user != null && passwordEncoder.matches(oldPassword, user.getPassword())) { + user.setPassword(passwordEncoder.encode(newPassword)); + userRepository.save(user); + return true; + } + return false; + } + + @Override + public Optional findById(Long id) { + return userRepository.findById(id); + } +} \ No newline at end of file diff --git a/src/test/application-test.properties b/src/test/application-test.properties new file mode 100644 index 0000000..77d29e8 --- /dev/null +++ b/src/test/application-test.properties @@ -0,0 +1,7 @@ +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=update +spring.h2.console.enabled=true \ No newline at end of file diff --git a/src/test/java/org/cmh/backend/BackendApplicationTests.java b/src/test/java/org/cmh/backend/BackendApplicationTests.java index 8bc26ca..e490cdf 100644 --- a/src/test/java/org/cmh/backend/BackendApplicationTests.java +++ b/src/test/java/org/cmh/backend/BackendApplicationTests.java @@ -2,14 +2,11 @@ package org.cmh.backend; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -@Import(TestcontainersConfiguration.class) @SpringBootTest -class BackendApplicationTests { +public class BackendApplicationTests { @Test void contextLoads() { } - -} +} \ No newline at end of file diff --git a/src/test/java/org/cmh/backend/TestBackendApplication.java b/src/test/java/org/cmh/backend/TestBackendApplication.java index a752147..52bb0ff 100644 --- a/src/test/java/org/cmh/backend/TestBackendApplication.java +++ b/src/test/java/org/cmh/backend/TestBackendApplication.java @@ -1,11 +1,12 @@ package org.cmh.backend; import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +@SpringBootApplication public class TestBackendApplication { public static void main(String[] args) { - SpringApplication.from(BackendApplication::main).with(TestcontainersConfiguration.class).run(args); + SpringApplication.run(TestBackendApplication.class, args); } - -} +} \ No newline at end of file