From 73061706d099bee4adefe158801c26acfc892df5 Mon Sep 17 00:00:00 2001 From: jiwon1217 Date: Tue, 15 Jul 2025 11:48:20 +0900 Subject: [PATCH 1/4] =?UTF-8?q?:sparkles:=20feat:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/api/UserController.java | 29 +++++++++++++++++++ .../backend/domain/user/app/UserService.java | 7 +++++ .../backend/global/config/SecurityConfig.java | 1 + .../f1/backend/global/config/WebConfig.java | 7 +++++ .../f1/backend/global/util/SecurityUtils.java | 12 ++++++++ 5 files changed, 56 insertions(+) create mode 100644 backend/src/main/java/io/f1/backend/domain/user/api/UserController.java diff --git a/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java new file mode 100644 index 00000000..aec10c0e --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java @@ -0,0 +1,29 @@ +package io.f1.backend.domain.user.api; + +import static io.f1.backend.global.util.SecurityUtils.logout; + +import io.f1.backend.domain.user.app.UserService; +import io.f1.backend.domain.user.dto.UserPrincipal; +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequiredArgsConstructor +@RequestMapping("/user/me") +public class UserController { + + private final UserService userService; + + @DeleteMapping + public ResponseEntity deleteCurrentUser( + @AuthenticationPrincipal UserPrincipal userPrincipal, HttpSession httpSession) { + userService.deleteUser(userPrincipal.getUserId()); + logout(httpSession); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java index c7e48e56..5ec852d7 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java @@ -82,4 +82,11 @@ private void updateSessionAfterSignup(HttpSession session, User user) { session.removeAttribute(OAUTH_USER); session.setAttribute(USER, AuthenticationUser.from(user)); } + + @Transactional + public void deleteUser(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); + userRepository.delete(user); + } } diff --git a/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java index ebcfc42e..eed86bb2 100644 --- a/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java +++ b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java @@ -42,6 +42,7 @@ public SecurityFilterChain userFilterChain(HttpSecurity http) throws Exception { .permitAll() .requestMatchers("/ws/**") .authenticated() + .requestMatchers("/user/me").hasRole("USER") .anyRequest() .authenticated()) .formLogin(AbstractHttpConfigurer::disable) diff --git a/backend/src/main/java/io/f1/backend/global/config/WebConfig.java b/backend/src/main/java/io/f1/backend/global/config/WebConfig.java index 8d093eec..a80ae64c 100644 --- a/backend/src/main/java/io/f1/backend/global/config/WebConfig.java +++ b/backend/src/main/java/io/f1/backend/global/config/WebConfig.java @@ -1,6 +1,8 @@ package io.f1.backend.global.config; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -14,4 +16,9 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/images/thumbnail/**") .addResourceLocations("file:images/thumbnail/"); } + + @Bean + public HiddenHttpMethodFilter hiddenHttpMethodFilter() { + return new HiddenHttpMethodFilter(); + } } diff --git a/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java index 5915e5d2..c2ad2ca1 100644 --- a/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java +++ b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java @@ -3,6 +3,7 @@ import io.f1.backend.domain.user.dto.UserPrincipal; import io.f1.backend.domain.user.entity.User; +import jakarta.servlet.http.HttpSession; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -37,4 +38,15 @@ public static Long getCurrentUserId() { public static String getCurrentUserNickname() { return getCurrentUserPrincipal().getUserNickname(); } + + public static void logout(HttpSession session) { + if (session != null) { + session.invalidate(); + } + clearAuthentication(); + } + + private static void clearAuthentication() { + SecurityContextHolder.clearContext(); + } } From 4f55482568afc9fec059302c712e1dafaf8b2258 Mon Sep 17 00:00:00 2001 From: jiwon1217 Date: Tue, 15 Jul 2025 11:52:14 +0900 Subject: [PATCH 2/4] =?UTF-8?q?:sparkles:=20feat:=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/api/UserController.java | 12 +++++++++ .../backend/domain/user/app/UserService.java | 27 ++++++++++++------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java index aec10c0e..d6a3791d 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java +++ b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java @@ -3,6 +3,7 @@ import static io.f1.backend.global.util.SecurityUtils.logout; import io.f1.backend.domain.user.app.UserService; +import io.f1.backend.domain.user.dto.SignupRequestDto; import io.f1.backend.domain.user.dto.UserPrincipal; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; @@ -10,6 +11,8 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @Controller @@ -26,4 +29,13 @@ public ResponseEntity deleteCurrentUser( logout(httpSession); return ResponseEntity.noContent().build(); } + + @PutMapping + public ResponseEntity updateNickname( + @AuthenticationPrincipal UserPrincipal userPrincipal, + @RequestBody SignupRequestDto signupRequest, HttpSession httpSession) { + userService.updateNickname(userPrincipal.getUserId(), signupRequest.nickname(), + httpSession); + return ResponseEntity.noContent().build(); + } } \ No newline at end of file diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java index 5ec852d7..9c12e952 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java @@ -10,11 +10,8 @@ import io.f1.backend.domain.user.dto.SignupResponseDto; import io.f1.backend.domain.user.entity.User; import io.f1.backend.global.util.SecurityUtils; - import jakarta.servlet.http.HttpSession; - import lombok.RequiredArgsConstructor; - import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,7 +29,7 @@ public SignupResponseDto signup(HttpSession session, SignupRequestDto signupRequ validateNicknameFormat(nickname); validateNicknameDuplicate(nickname); - User user = updateUserNickname(authenticationUser.userId(), nickname); + User user = initNickname(authenticationUser.userId(), nickname); updateSessionAfterSignup(session, user); SecurityUtils.setAuthentication(user); @@ -41,7 +38,7 @@ public SignupResponseDto signup(HttpSession session, SignupRequestDto signupRequ private AuthenticationUser extractSessionUser(HttpSession session) { AuthenticationUser authenticationUser = - (AuthenticationUser) session.getAttribute(OAUTH_USER); + (AuthenticationUser) session.getAttribute(OAUTH_USER); if (authenticationUser == null) { throw new RuntimeException("E401001: 로그인이 필요합니다."); } @@ -68,11 +65,11 @@ public void validateNicknameDuplicate(String nickname) { } @Transactional - public User updateUserNickname(Long userId, String nickname) { + public User initNickname(Long userId, String nickname) { User user = - userRepository - .findById(userId) - .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); + userRepository + .findById(userId) + .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); user.updateNickname(nickname); return userRepository.save(user); @@ -89,4 +86,14 @@ public void deleteUser(Long userId) { .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); userRepository.delete(user); } -} + + @Transactional + public void updateNickname(Long userId, String newNickname, HttpSession session) { + validateNicknameFormat(newNickname); + validateNicknameDuplicate(newNickname); + + User user = initNickname(userId, newNickname); + session.setAttribute(USER, AuthenticationUser.from(user)); + SecurityUtils.setAuthentication(user); + } +} \ No newline at end of file From 8d2ee1927fd7e170928158e1ef51dd37c5e55d7b Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Tue, 15 Jul 2025 02:53:34 +0000 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/api/UserController.java | 16 ++++++++++------ .../backend/domain/user/app/UserService.java | 19 ++++++++++++------- .../backend/global/config/SecurityConfig.java | 3 ++- .../f1/backend/global/util/SecurityUtils.java | 1 + 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java index d6a3791d..150ea6f0 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java +++ b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java @@ -5,8 +5,11 @@ import io.f1.backend.domain.user.app.UserService; import io.f1.backend.domain.user.dto.SignupRequestDto; import io.f1.backend.domain.user.dto.UserPrincipal; + import jakarta.servlet.http.HttpSession; + import lombok.RequiredArgsConstructor; + import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; @@ -24,7 +27,7 @@ public class UserController { @DeleteMapping public ResponseEntity deleteCurrentUser( - @AuthenticationPrincipal UserPrincipal userPrincipal, HttpSession httpSession) { + @AuthenticationPrincipal UserPrincipal userPrincipal, HttpSession httpSession) { userService.deleteUser(userPrincipal.getUserId()); logout(httpSession); return ResponseEntity.noContent().build(); @@ -32,10 +35,11 @@ public ResponseEntity deleteCurrentUser( @PutMapping public ResponseEntity updateNickname( - @AuthenticationPrincipal UserPrincipal userPrincipal, - @RequestBody SignupRequestDto signupRequest, HttpSession httpSession) { - userService.updateNickname(userPrincipal.getUserId(), signupRequest.nickname(), - httpSession); + @AuthenticationPrincipal UserPrincipal userPrincipal, + @RequestBody SignupRequestDto signupRequest, + HttpSession httpSession) { + userService.updateNickname( + userPrincipal.getUserId(), signupRequest.nickname(), httpSession); return ResponseEntity.noContent().build(); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java index 9c12e952..f23e2b0f 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java @@ -10,8 +10,11 @@ import io.f1.backend.domain.user.dto.SignupResponseDto; import io.f1.backend.domain.user.entity.User; import io.f1.backend.global.util.SecurityUtils; + import jakarta.servlet.http.HttpSession; + import lombok.RequiredArgsConstructor; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -38,7 +41,7 @@ public SignupResponseDto signup(HttpSession session, SignupRequestDto signupRequ private AuthenticationUser extractSessionUser(HttpSession session) { AuthenticationUser authenticationUser = - (AuthenticationUser) session.getAttribute(OAUTH_USER); + (AuthenticationUser) session.getAttribute(OAUTH_USER); if (authenticationUser == null) { throw new RuntimeException("E401001: 로그인이 필요합니다."); } @@ -67,9 +70,9 @@ public void validateNicknameDuplicate(String nickname) { @Transactional public User initNickname(Long userId, String nickname) { User user = - userRepository - .findById(userId) - .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); + userRepository + .findById(userId) + .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); user.updateNickname(nickname); return userRepository.save(user); @@ -82,8 +85,10 @@ private void updateSessionAfterSignup(HttpSession session, User user) { @Transactional public void deleteUser(Long userId) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); + User user = + userRepository + .findById(userId) + .orElseThrow(() -> new RuntimeException("E404001: 존재하지 않는 회원입니다.")); userRepository.delete(user); } @@ -96,4 +101,4 @@ public void updateNickname(Long userId, String newNickname, HttpSession session) session.setAttribute(USER, AuthenticationUser.from(user)); SecurityUtils.setAuthentication(user); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java index eed86bb2..2866dc7e 100644 --- a/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java +++ b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java @@ -42,7 +42,8 @@ public SecurityFilterChain userFilterChain(HttpSecurity http) throws Exception { .permitAll() .requestMatchers("/ws/**") .authenticated() - .requestMatchers("/user/me").hasRole("USER") + .requestMatchers("/user/me") + .hasRole("USER") .anyRequest() .authenticated()) .formLogin(AbstractHttpConfigurer::disable) diff --git a/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java index c2ad2ca1..9a4530a0 100644 --- a/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java +++ b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java @@ -4,6 +4,7 @@ import io.f1.backend.domain.user.entity.User; import jakarta.servlet.http.HttpSession; + import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; From 5ed35cc1c3500c739e7a03ddaff355abfc1b1d91 Mon Sep 17 00:00:00 2001 From: jiwon1217 Date: Tue, 15 Jul 2025 16:37:30 +0900 Subject: [PATCH 4/4] =?UTF-8?q?:bug:=20fix:=20@Controller=20->=20@RestCont?= =?UTF-8?q?roller=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/f1/backend/domain/user/api/UserController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java index 150ea6f0..4cbe5343 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java +++ b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java @@ -12,13 +12,13 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -@Controller +@RestController @RequiredArgsConstructor @RequestMapping("/user/me") public class UserController {