From 537f8ce4ebfa258fc0123d177b41f0dc9b95a0a5 Mon Sep 17 00:00:00 2001 From: youjin Date: Sun, 29 Dec 2024 23:10:13 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=EC=98=A4=EB=A5=98=EB=A5=BC=20=EA=B3=A0?= =?UTF-8?q?=EC=B3=90=EB=B3=B4=EC=95=84=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/redunm/config/SecurityConfig.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/example/redunm/config/SecurityConfig.java b/src/main/java/com/example/redunm/config/SecurityConfig.java index 7af63db..d3dbff7 100644 --- a/src/main/java/com/example/redunm/config/SecurityConfig.java +++ b/src/main/java/com/example/redunm/config/SecurityConfig.java @@ -2,11 +2,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -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; import org.springframework.security.web.SecurityFilterChain; @@ -19,33 +21,20 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .csrf(AbstractHttpConfigurer::disable) // CSRF 비활성화 - .cors(Customizer.withDefaults()) // 기본 CORS 설정 - + .csrf(csrf -> csrf.disable()) // CSRF 비활성화 + .cors(Customizer.withDefaults()) // 기본 CORS 설정 + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers( - "/api/auth/signup/**", // 회원가입 관련 요청 허용 - "/api/auth/login/**", // 로그인 관련 요청 허용 - "/api/cart/**", // 장바구니 API 요청 허용 - "/api/data-models/**", // 데이터 모델 API 요청 허용 - "/css/**", // 정적 리소스 허용 + "/api/auth/signup/**", + "/api/auth/login/**", + "/api/cart/**", + "/api/data-models/**", + "/css/**", "/js/**", "/models/**" ).permitAll() - .anyRequest().authenticated() // 그 외의 요청은 인증 필요 - ) - - .formLogin(form -> form - .loginPage("/login") // 커스텀 로그인 페이지 - .loginProcessingUrl("/api/auth/login") // 로그인 요청 URL - .defaultSuccessUrl("/home", true) // 로그인 성공 후 이동 URL - .permitAll() - ) - - .logout(logout -> logout - .logoutUrl("/api/auth/logout") // 로그아웃 URL - .logoutSuccessUrl("/") // 로그아웃 성공 후 이동 URL - .permitAll() + .anyRequest().authenticated() ); return http.build(); @@ -55,4 +44,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { + return authConfig.getAuthenticationManager(); + } } From 531cdbd09fe0eb2dd64cbeefeee4ed3a3cbfa249 Mon Sep 17 00:00:00 2001 From: youjin Date: Sun, 29 Dec 2024 23:58:06 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=EC=95=84=EB=8B=88=20=EA=B0=91=EC=A7=80?= =?UTF-8?q?=EA=B8=B0=20=EC=99=9C....?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/redunm/config/SecurityConfig.java | 54 +++++++++----- .../com/example/redunm/config/WebConfig.java | 25 +++---- .../java/com/example/redunm/entity/User.java | 36 +++++++++- .../exception/GlobalExceptionHandler.java | 24 +++++-- .../filter/JsonAuthenticationFilter.java | 63 ++++++++++++++++ .../example/redunm/login/LoginController.java | 71 ------------------- .../example/redunm/login/LoginRequest.java | 9 ++- .../example/redunm/login/LoginResponse.java | 18 +++++ .../redunm/repository/UserRepository.java | 3 - .../example/redunm/service/UserService.java | 22 +++--- ...nUpController.java => AuthController.java} | 19 ++--- 11 files changed, 208 insertions(+), 136 deletions(-) create mode 100644 src/main/java/com/example/redunm/filter/JsonAuthenticationFilter.java delete mode 100644 src/main/java/com/example/redunm/login/LoginController.java create mode 100644 src/main/java/com/example/redunm/login/LoginResponse.java rename src/main/java/com/example/redunm/signup/{SignUpController.java => AuthController.java} (76%) diff --git a/src/main/java/com/example/redunm/config/SecurityConfig.java b/src/main/java/com/example/redunm/config/SecurityConfig.java index d3dbff7..a400404 100644 --- a/src/main/java/com/example/redunm/config/SecurityConfig.java +++ b/src/main/java/com/example/redunm/config/SecurityConfig.java @@ -1,29 +1,52 @@ package com.example.redunm.config; +import com.example.redunm.filter.JsonAuthenticationFilter; +import com.example.redunm.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity @EnableMethodSecurity public class SecurityConfig { + private final UserService userService; + + @Autowired + public SecurityConfig(UserService userService) { + this.userService = userService; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); + } + @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authManager) throws Exception { http - .csrf(csrf -> csrf.disable()) // CSRF 비활성화 - .cors(Customizer.withDefaults()) // 기본 CORS 설정 - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .csrf(csrf -> csrf.disable()) + .cors(Customizer.withDefaults()) + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 세션 사용 + ) .authorizeHttpRequests(auth -> auth .requestMatchers( "/api/auth/signup/**", @@ -35,18 +58,17 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/models/**" ).permitAll() .anyRequest().authenticated() - ); + ) + .logout(logout -> logout + .logoutUrl("/api/auth/logout") + .logoutSuccessUrl("/") + .permitAll() + ) + .formLogin(form -> form.disable()); - return http.build(); - } + JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter("/api/auth/login", authManager, userService); + http.addFilterBefore(jsonAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { - return authConfig.getAuthenticationManager(); + return http.build(); } } diff --git a/src/main/java/com/example/redunm/config/WebConfig.java b/src/main/java/com/example/redunm/config/WebConfig.java index b70ccbd..415be0e 100644 --- a/src/main/java/com/example/redunm/config/WebConfig.java +++ b/src/main/java/com/example/redunm/config/WebConfig.java @@ -1,24 +1,17 @@ package com.example.redunm.config; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.config.annotation.*; @Configuration -public class WebConfig { +public class WebConfig implements WebMvcConfigurer { - @Bean - public WebMvcConfigurer corsConfigurer() { - return new WebMvcConfigurer() { - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins("http://192.168.0.20:3000", "http://localhost:3000") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .allowCredentials(true); - } - }; + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("http://localhost:3000") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); } } diff --git a/src/main/java/com/example/redunm/entity/User.java b/src/main/java/com/example/redunm/entity/User.java index f9c80ee..b0d7172 100644 --- a/src/main/java/com/example/redunm/entity/User.java +++ b/src/main/java/com/example/redunm/entity/User.java @@ -2,9 +2,13 @@ import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; @Document(collection = "users") -public class User { +public class User implements UserDetails { @Id private String id; @@ -23,19 +27,20 @@ public void setId(String id) { this.id = id; } + @Override public String getUsername() { - return username; + return email; } public void setUsername(String username) { this.username = username; } + @Override public String getPassword() { return password; } - // 비밀번호는 암호화된 상태로 설정됨 public void setPassword(String password) { this.password = password; } @@ -55,4 +60,29 @@ public String getPhone() { public void setPhone(String phone) { this.phone = phone; } + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } } diff --git a/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java b/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java index 518aa1c..c703f1f 100644 --- a/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java @@ -2,17 +2,27 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.*; -import org.springframework.web.HttpRequestMethodNotSupportedException; -@ControllerAdvice +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice public class GlobalExceptionHandler { - @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - public ResponseEntity handleMethodNotSupported(HttpRequestMethodNotSupportedException ex) { - return ResponseEntity - .status(HttpStatus.METHOD_NOT_ALLOWED) - .body("지원되지 않는 HTTP 메서드입니다. " + ex.getMethod() + "이 엔드포인트에서 지원되지 않습니다."); + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationErrors(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + + ex.getBindingResult().getAllErrors().forEach((error) -> { + String fieldName = ((FieldError) error).getField(); + String errorMessage = error.getDefaultMessage(); + errors.put(fieldName, errorMessage); + }); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors); } } diff --git a/src/main/java/com/example/redunm/filter/JsonAuthenticationFilter.java b/src/main/java/com/example/redunm/filter/JsonAuthenticationFilter.java new file mode 100644 index 0000000..7c82e5b --- /dev/null +++ b/src/main/java/com/example/redunm/filter/JsonAuthenticationFilter.java @@ -0,0 +1,63 @@ +package com.example.redunm.filter; + +import com.example.redunm.login.LoginRequest; +import com.example.redunm.service.UserService; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.io.IOException; + +public class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final UserService userService; + + public JsonAuthenticationFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager, UserService userService) { + super(defaultFilterProcessesUrl); + setAuthenticationManager(authenticationManager); + this.userService = userService; + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException, IOException, ServletException { + + if (!request.getMethod().equals("POST")) { + throw new ServletException("Authentication method not supported: " + request.getMethod()); + } + + LoginRequest loginRequest = objectMapper.readValue(request.getInputStream(), LoginRequest.class); + + UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( + loginRequest.getEmail(), + loginRequest.getPassword() + ); + + return getAuthenticationManager().authenticate(authRequest); + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, + Authentication authResult) throws IOException, ServletException { + SecurityContextHolder.getContext().setAuthentication(authResult); + response.setContentType("application/json"); + response.getWriter().write("{\"message\": \"로그인 성공\"}"); + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, + AuthenticationException failed) throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json"); + response.getWriter().write("{\"error\": \"이메일 또는 비밀번호가 잘못되었습니다.\"}"); + } +} diff --git a/src/main/java/com/example/redunm/login/LoginController.java b/src/main/java/com/example/redunm/login/LoginController.java deleted file mode 100644 index c305875..0000000 --- a/src/main/java/com/example/redunm/login/LoginController.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.example.redunm.login; - -import com.example.redunm.entity.User; -import com.example.redunm.service.UserService; -import com.example.redunm.login.LoginRequest; -import jakarta.servlet.http.HttpSession; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.web.bind.annotation.*; - -import java.util.Map; - -@RestController -@RequestMapping("/api/auth/login") -public class LoginController { - - @Autowired - private UserService userService; - - private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - - // POST 요청 - @PostMapping - public ResponseEntity login(@RequestBody LoginRequest loginRequest, - HttpSession session) { - String email = loginRequest.getEmail(); - String password = loginRequest.getPassword(); - - if (email == null || password == null) { - return ResponseEntity - .status(HttpStatus.BAD_REQUEST) - .body("이메일과 비밀번호를 모두 입력해주세요."); - } - - var optionalUser = userService.findByEmail(email); - if (optionalUser.isEmpty()) { - return ResponseEntity - .status(HttpStatus.BAD_REQUEST) - .body("존재하지 않는 이메일입니다."); - } - - User user = optionalUser.get(); - - if (!passwordEncoder.matches(password, user.getPassword())) { - return ResponseEntity - .status(HttpStatus.BAD_REQUEST) - .body("비밀번호가 일치하지 않습니다."); - } - - session.setAttribute("loggedInUser", user); - - return ResponseEntity.ok("로그인 성공"); - } - - // GET 요청에 대한 처리 추가 - @GetMapping - public ResponseEntity handleGetLogin() { - return ResponseEntity - .status(HttpStatus.METHOD_NOT_ALLOWED) - .body("GET 메서드는 /api/auth/login 엔드포인트에서 지원되지 않습니다. POST 메서드를 사용하세요."); - } - - // 로그아웃 처리 (POST 방식) - @PostMapping("/logout") - public ResponseEntity logout(HttpSession session) { - session.invalidate(); - return ResponseEntity.ok("로그아웃 성공"); - } -} diff --git a/src/main/java/com/example/redunm/login/LoginRequest.java b/src/main/java/com/example/redunm/login/LoginRequest.java index 5c2f933..a7fa47e 100644 --- a/src/main/java/com/example/redunm/login/LoginRequest.java +++ b/src/main/java/com/example/redunm/login/LoginRequest.java @@ -1,9 +1,16 @@ package com.example.redunm.login; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; + public class LoginRequest { + + @NotBlank(message = "이메일은 필수입니다.") + @Email(message = "유효한 이메일 형식을 입력해주세요.") private String email; - private String password; + @NotBlank(message = "비밀번호는 필수입니다.") + private String password; public String getEmail() { return email; diff --git a/src/main/java/com/example/redunm/login/LoginResponse.java b/src/main/java/com/example/redunm/login/LoginResponse.java new file mode 100644 index 0000000..4d62723 --- /dev/null +++ b/src/main/java/com/example/redunm/login/LoginResponse.java @@ -0,0 +1,18 @@ +package com.example.redunm.login; + +public class LoginResponse { + private String message; + + public LoginResponse(String message) { + this.message = message; + } + + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/main/java/com/example/redunm/repository/UserRepository.java b/src/main/java/com/example/redunm/repository/UserRepository.java index 55c8d5c..f46942c 100644 --- a/src/main/java/com/example/redunm/repository/UserRepository.java +++ b/src/main/java/com/example/redunm/repository/UserRepository.java @@ -2,11 +2,8 @@ import com.example.redunm.entity.User; import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.stereotype.Repository; - import java.util.Optional; -@Repository public interface UserRepository extends MongoRepository { Optional findByUsername(String username); Optional findByEmail(String email); diff --git a/src/main/java/com/example/redunm/service/UserService.java b/src/main/java/com/example/redunm/service/UserService.java index 8399f84..33bfc7c 100644 --- a/src/main/java/com/example/redunm/service/UserService.java +++ b/src/main/java/com/example/redunm/service/UserService.java @@ -3,13 +3,14 @@ import com.example.redunm.entity.User; import com.example.redunm.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.*; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.Optional; @Service -public class UserService { +public class UserService implements UserDetailsService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; @@ -38,15 +39,14 @@ public User save(User user) { } public boolean isDuplicate(User user) { - if (findByUsername(user.getUsername()).isPresent()) { - return true; - } - if (findByEmail(user.getEmail()).isPresent()) { - return true; - } - if (findByPhone(user.getPhone()).isPresent()) { - return true; - } - return false; + return findByUsername(user.getUsername()).isPresent() + || findByEmail(user.getEmail()).isPresent() + || findByPhone(user.getPhone()).isPresent(); + } + + @Override + public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + return userRepository.findByEmail(email) + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); } } diff --git a/src/main/java/com/example/redunm/signup/SignUpController.java b/src/main/java/com/example/redunm/signup/AuthController.java similarity index 76% rename from src/main/java/com/example/redunm/signup/SignUpController.java rename to src/main/java/com/example/redunm/signup/AuthController.java index 2c1d54e..635e860 100644 --- a/src/main/java/com/example/redunm/signup/SignUpController.java +++ b/src/main/java/com/example/redunm/signup/AuthController.java @@ -13,17 +13,18 @@ import java.util.Map; @RestController -@RequestMapping("/api/auth/signup") -public class SignUpController { +@RequestMapping("/api/auth") +public class AuthController { private final UserService userService; @Autowired - public SignUpController(UserService userService) { + public AuthController(UserService userService) { this.userService = userService; } - @PostMapping + // 회원가입 엔드포인트 + @PostMapping("/signup") public ResponseEntity signup(@Valid @RequestBody SignUpRequest signUpRequest) { if (!signUpRequest.getPassword().equals(signUpRequest.getConfirmPassword())) { return ResponseEntity @@ -51,7 +52,7 @@ public ResponseEntity signup(@Valid @RequestBody SignUpRequest signUpRequest) User user = new User(); user.setUsername(signUpRequest.getUsername()); - user.setPassword(signUpRequest.getPassword()); + user.setPassword(signUpRequest.getPassword()); // PasswordEncoder는 UserService.save()에서 처리됨 user.setEmail(signUpRequest.getEmail()); user.setPhone(signUpRequest.getPhone()); @@ -63,10 +64,12 @@ public ResponseEntity signup(@Valid @RequestBody SignUpRequest signUpRequest) return ResponseEntity.ok(response); } - @GetMapping("/success") - public ResponseEntity success() { + // 로그아웃 엔드포인트 (Spring Security에서 처리) + @PostMapping("/logout") + public ResponseEntity logout() { + // 로그아웃은 SecurityConfig에서 처리되므로, 별도의 로직이 필요 없습니다. Map response = new HashMap<>(); - response.put("message", "회원가입이 성공적으로 완료되었습니다."); + response.put("message", "로그아웃이 성공적으로 완료되었습니다."); return ResponseEntity.ok(response); } } From 7b2a8383eb39e77d413fac846a80522565d71f0e Mon Sep 17 00:00:00 2001 From: youjin Date: Mon, 30 Dec 2024 00:00:16 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix=20:=20=EC=88=9C=ED=99=98=EC=B0=B8?= =?UTF-8?q?=EC=A1=B0=EA=B4=80=EB=A0=A8=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/redunm/config/PasswordConfig.java | 15 +++++++++++++++ .../com/example/redunm/config/SecurityConfig.java | 9 +-------- 2 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/example/redunm/config/PasswordConfig.java diff --git a/src/main/java/com/example/redunm/config/PasswordConfig.java b/src/main/java/com/example/redunm/config/PasswordConfig.java new file mode 100644 index 0000000..cb0502d --- /dev/null +++ b/src/main/java/com/example/redunm/config/PasswordConfig.java @@ -0,0 +1,15 @@ +package com.example.redunm.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/example/redunm/config/SecurityConfig.java b/src/main/java/com/example/redunm/config/SecurityConfig.java index a400404..05d5d10 100644 --- a/src/main/java/com/example/redunm/config/SecurityConfig.java +++ b/src/main/java/com/example/redunm/config/SecurityConfig.java @@ -11,8 +11,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -29,11 +27,6 @@ public SecurityConfig(UserService userService) { this.userService = userService; } - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); @@ -45,7 +38,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication .csrf(csrf -> csrf.disable()) .cors(Customizer.withDefaults()) .sessionManagement(session -> session - .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 세션 사용 + .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ) .authorizeHttpRequests(auth -> auth .requestMatchers( From 79ec8b22f687015493f4feb27d3192795972ddbd Mon Sep 17 00:00:00 2001 From: youjin Date: Mon, 30 Dec 2024 00:06:48 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix=20:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=20=EC=A0=91=EA=B7=BC=20=EA=B6=8C=ED=99=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/redunm/config/SecurityConfig.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/redunm/config/SecurityConfig.java b/src/main/java/com/example/redunm/config/SecurityConfig.java index 05d5d10..0810cd3 100644 --- a/src/main/java/com/example/redunm/config/SecurityConfig.java +++ b/src/main/java/com/example/redunm/config/SecurityConfig.java @@ -11,6 +11,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -27,18 +29,27 @@ public SecurityConfig(UserService userService) { this.userService = userService; } + // PasswordEncoder Bean + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + // AuthenticationManager Bean 정의 @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } + // SecurityFilterChain Bean 정의 @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authManager) throws Exception { + // CSRF 비활성화 및 CORS 설정 http - .csrf(csrf -> csrf.disable()) + .csrf(csrf -> csrf.disable()) // CSRF 비활성화 .cors(Customizer.withDefaults()) .sessionManagement(session -> session - .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) + .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 세션 사용 ) .authorizeHttpRequests(auth -> auth .requestMatchers( @@ -49,16 +60,18 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication "/css/**", "/js/**", "/models/**" - ).permitAll() - .anyRequest().authenticated() + ).permitAll() // 이 경로들은 인증 없이 접근 가능 + .anyRequest().authenticated() // 나머지 경로들은 인증 필요 ) .logout(logout -> logout .logoutUrl("/api/auth/logout") .logoutSuccessUrl("/") .permitAll() ) + // 기본 formLogin 비활성화 .formLogin(form -> form.disable()); + // 커스텀 JSON 인증 필터 추가 JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter("/api/auth/login", authManager, userService); http.addFilterBefore(jsonAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); From 84926d2dbc2dc436215d460d62d3aa34a84e5064 Mon Sep 17 00:00:00 2001 From: youjin Date: Mon, 30 Dec 2024 00:29:31 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix=20:=20400error=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/redunm/config/PasswordConfig.java | 30 +++++++++---------- .../example/redunm/config/SecurityConfig.java | 17 ++--------- .../com/example/redunm/dto/SignUpRequest.java | 18 +++++------ .../exception/GlobalExceptionHandler.java | 11 +++++-- .../example/redunm/signup/AuthController.java | 1 - 5 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/example/redunm/config/PasswordConfig.java b/src/main/java/com/example/redunm/config/PasswordConfig.java index cb0502d..17e4cd5 100644 --- a/src/main/java/com/example/redunm/config/PasswordConfig.java +++ b/src/main/java/com/example/redunm/config/PasswordConfig.java @@ -1,15 +1,15 @@ -package com.example.redunm.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; - -@Configuration -public class PasswordConfig { - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} +package com.example.redunm.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/example/redunm/config/SecurityConfig.java b/src/main/java/com/example/redunm/config/SecurityConfig.java index 0810cd3..7d253eb 100644 --- a/src/main/java/com/example/redunm/config/SecurityConfig.java +++ b/src/main/java/com/example/redunm/config/SecurityConfig.java @@ -29,27 +29,18 @@ public SecurityConfig(UserService userService) { this.userService = userService; } - // PasswordEncoder Bean - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - // AuthenticationManager Bean 정의 @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } - // SecurityFilterChain Bean 정의 @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authManager) throws Exception { - // CSRF 비활성화 및 CORS 설정 http .csrf(csrf -> csrf.disable()) // CSRF 비활성화 .cors(Customizer.withDefaults()) .sessionManagement(session -> session - .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 세션 사용 + .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ) .authorizeHttpRequests(auth -> auth .requestMatchers( @@ -60,18 +51,16 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication "/css/**", "/js/**", "/models/**" - ).permitAll() // 이 경로들은 인증 없이 접근 가능 - .anyRequest().authenticated() // 나머지 경로들은 인증 필요 + ).permitAll() + .anyRequest().authenticated() ) .logout(logout -> logout .logoutUrl("/api/auth/logout") .logoutSuccessUrl("/") .permitAll() ) - // 기본 formLogin 비활성화 .formLogin(form -> form.disable()); - // 커스텀 JSON 인증 필터 추가 JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter("/api/auth/login", authManager, userService); http.addFilterBefore(jsonAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/src/main/java/com/example/redunm/dto/SignUpRequest.java b/src/main/java/com/example/redunm/dto/SignUpRequest.java index 74897a6..2965014 100644 --- a/src/main/java/com/example/redunm/dto/SignUpRequest.java +++ b/src/main/java/com/example/redunm/dto/SignUpRequest.java @@ -2,29 +2,25 @@ import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; public class SignUpRequest { - @NotBlank(message = "아이디는 필수입니다.") + @NotBlank(message = "Username is mandatory") private String username; - @NotBlank(message = "비밀번호는 필수입니다.") - @Size(min = 6, message = "비밀번호는 최소 6자 이상이어야 합니다.") - @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{6,}$", - message = "비밀번호는 대문자, 소문자, 숫자, 특수문자를 포함해야 합니다.") + @NotBlank(message = "Password is mandatory") + @Size(min = 8, message = "Password must be at least 8 characters") private String password; - @NotBlank(message = "비밀번호 확인은 필수입니다.") + @NotBlank(message = "Confirm Password is mandatory") private String confirmPassword; - @NotBlank(message = "이메일은 필수입니다.") - @Email(message = "유효한 이메일 형식을 입력해주세요.") + @NotBlank(message = "Email is mandatory") + @Email(message = "Email should be valid") private String email; - @NotBlank(message = "전화번호는 필수입니다.") - @Pattern(regexp = "^\\d{10,15}$", message = "유효한 전화번호를 입력해주세요.") + @NotBlank(message = "Phone number is mandatory") private String phone; // Getters and Setters diff --git a/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java b/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java index c703f1f..84e2166 100644 --- a/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java @@ -13,7 +13,8 @@ public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleValidationErrors(MethodArgumentNotValidException ex) { + public ResponseEntity> handleValidationExceptions( + MethodArgumentNotValidException ex) { Map errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach((error) -> { @@ -22,7 +23,13 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidException errors.put(fieldName, errorMessage); }); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); } + @ExceptionHandler(Exception.class) + public ResponseEntity> handleAllExceptions(Exception ex) { + Map error = new HashMap<>(); + error.put("message", ex.getMessage()); + return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); + } } diff --git a/src/main/java/com/example/redunm/signup/AuthController.java b/src/main/java/com/example/redunm/signup/AuthController.java index 635e860..fdc415a 100644 --- a/src/main/java/com/example/redunm/signup/AuthController.java +++ b/src/main/java/com/example/redunm/signup/AuthController.java @@ -23,7 +23,6 @@ public AuthController(UserService userService) { this.userService = userService; } - // 회원가입 엔드포인트 @PostMapping("/signup") public ResponseEntity signup(@Valid @RequestBody SignUpRequest signUpRequest) { if (!signUpRequest.getPassword().equals(signUpRequest.getConfirmPassword())) {