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..bfc38f6e --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/api/UserController.java @@ -0,0 +1,41 @@ +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.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; +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 +@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(); + } + + @PutMapping + public ResponseEntity updateNickname( + @AuthenticationPrincipal UserPrincipal userPrincipal, + @RequestBody SignupRequestDto signupRequest, HttpSession httpSession) { + userService.updateNickname(userPrincipal.getUserId(), signupRequest.nickname(), + httpSession); + return ResponseEntity.noContent().build(); + } +} 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..b33bfb72 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); @@ -82,4 +79,21 @@ 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); + } + + @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); + } } 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(); + } }