From 33a70cf451917a647e8aa4cdf1618568a3eca85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=9A=A9=EC=88=98?= Date: Tue, 24 Sep 2024 13:19:51 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20FCM=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthController.java | 29 ++++++++++---- .../auth/handler/CustomLogoutHandler.java | 38 ------------------- .../domain/auth/repository/FcmRepository.java | 6 +-- .../global/config/SecurityConfig.java | 6 +-- 4 files changed, 26 insertions(+), 53 deletions(-) delete mode 100644 backend/src/main/java/com/worlabel/domain/auth/handler/CustomLogoutHandler.java diff --git a/backend/src/main/java/com/worlabel/domain/auth/controller/AuthController.java b/backend/src/main/java/com/worlabel/domain/auth/controller/AuthController.java index ac9ef9b..8209be8 100644 --- a/backend/src/main/java/com/worlabel/domain/auth/controller/AuthController.java +++ b/backend/src/main/java/com/worlabel/domain/auth/controller/AuthController.java @@ -12,7 +12,6 @@ import com.worlabel.global.config.swagger.SwaggerApiError; import com.worlabel.global.config.swagger.SwaggerApiSuccess; import com.worlabel.global.exception.CustomException; import com.worlabel.global.exception.ErrorCode; -import com.worlabel.global.service.FcmService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.Cookie; @@ -51,8 +50,8 @@ public class AuthController { JwtToken newToken = authService.reissue(refresh); int id = jwtTokenService.parseId(newToken.getAccessToken()); - response.addCookie(createCookie(newToken.getRefreshToken())); - authService.saveRefreshToken(id, newToken.getRefreshToken(),refreshExpiry); + response.addCookie(createRefreshCookie(newToken.getRefreshToken(), (int) (refreshExpiry / 1000))); + authService.saveRefreshToken(id, newToken.getRefreshToken(), refreshExpiry); return AccessTokenResponse.from(newToken.getAccessToken()); } catch (CustomException e) { @@ -62,11 +61,25 @@ public class AuthController { } } + @Operation(summary = "로그아웃", description = "사용자의 JWT, FCM Token, 리프레시 토큰을 삭제합니다.") + @SwaggerApiSuccess(description = "Logout") + @SwaggerApiError({ErrorCode.INVALID_TOKEN, ErrorCode.INVALID_REFRESH_TOKEN, ErrorCode.USER_NOT_FOUND}) + @PostMapping("/logout") + public void logout(@CurrentUser final Integer memberId, HttpServletRequest request, HttpServletResponse response) { + // 쿠키에서 리프레시 토큰 삭제 + Cookie deleteCookie = createRefreshCookie(null, 0); + response.addCookie(deleteCookie); + + authService.deleteRefreshToken(memberId); + authService.deleteFcmToken(memberId); + } + + @Operation(summary = "로그인 중인 사용자 정보를 반환", description = "현재 로그인중인 사용자의 정보를 반환합니다.") @SwaggerApiSuccess(description = "Return Member Info") @SwaggerApiError({ErrorCode.INVALID_TOKEN, ErrorCode.INVALID_REFRESH_TOKEN, ErrorCode.USER_NOT_FOUND}) @GetMapping("/profile") - public MemberResponse getMemberInfo(@CurrentUser final Integer currentMember){ + public MemberResponse getMemberInfo(@CurrentUser final Integer currentMember) { return memberService.getMemberId(currentMember); } @@ -74,13 +87,13 @@ public class AuthController { @SwaggerApiSuccess(description = "Redis에 FCM 토큰이 저장됨") @SwaggerApiError({ErrorCode.INVALID_TOKEN, ErrorCode.INVALID_REFRESH_TOKEN, ErrorCode.USER_NOT_FOUND}) @PostMapping("/fcm") - public void saveFcmToken(@CurrentUser final Integer currentMember, @RequestBody final FcmTokenRequest tokenRequest){ + public void saveFcmToken(@CurrentUser final Integer currentMember, @RequestBody final FcmTokenRequest tokenRequest) { authService.saveFcmToken(currentMember, tokenRequest.getToken()); } private static String parseRefreshCookie(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); - if(cookies != null) { + if (cookies != null) { return Arrays.stream(cookies) .filter(cookie -> "refreshToken".equals(cookie.getName())) .findFirst() @@ -90,9 +103,9 @@ public class AuthController { return null; } - private Cookie createCookie(String value) { + private Cookie createRefreshCookie(String value, int time) { Cookie cookie = new Cookie("refreshToken", value); - cookie.setMaxAge((int) (refreshExpiry / 1000)); + cookie.setMaxAge(time); cookie.setPath("/"); cookie.setHttpOnly(true); cookie.setSecure(true); // 배포 시 HTTPS에서 사용 diff --git a/backend/src/main/java/com/worlabel/domain/auth/handler/CustomLogoutHandler.java b/backend/src/main/java/com/worlabel/domain/auth/handler/CustomLogoutHandler.java deleted file mode 100644 index 6863f46..0000000 --- a/backend/src/main/java/com/worlabel/domain/auth/handler/CustomLogoutHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.worlabel.domain.auth.handler; - -import com.worlabel.domain.auth.service.AuthService; -import com.worlabel.domain.auth.service.JwtTokenService; -import com.worlabel.global.exception.CustomException; -import com.worlabel.global.exception.ErrorCode; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -@RequiredArgsConstructor -public class CustomLogoutHandler implements LogoutHandler { - - private AuthService authService; - private JwtTokenService jwtTokenService; - - @Override - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - String token = request.getHeader("Authorization"); - try { - String refreshedToken = token.substring(7); - int memberId = jwtTokenService.parseId(refreshedToken); - - authService.deleteRefreshToken(memberId); - authService.deleteFcmToken(memberId); - - log.debug("로그아웃된 사용자의 토큰이 삭제 됨 {}", memberId); - } catch (Exception e) { - throw new CustomException(ErrorCode.INVALID_REFRESH_TOKEN); - } - } -} diff --git a/backend/src/main/java/com/worlabel/domain/auth/repository/FcmRepository.java b/backend/src/main/java/com/worlabel/domain/auth/repository/FcmRepository.java index a394791..48a7e62 100644 --- a/backend/src/main/java/com/worlabel/domain/auth/repository/FcmRepository.java +++ b/backend/src/main/java/com/worlabel/domain/auth/repository/FcmRepository.java @@ -14,14 +14,14 @@ public class FcmRepository { private final RedisTemplate redisTemplate; public void save(int memberId, String token) { - redisTemplate.opsForHash().put(CacheKey.fcmTokenKey(), memberId, token); + redisTemplate.opsForHash().put(CacheKey.fcmTokenKey(), String.valueOf(memberId), token); } public void delete(int memberId) { - redisTemplate.opsForHash().delete(CacheKey.fcmTokenKey(), memberId); + redisTemplate.opsForHash().delete(CacheKey.fcmTokenKey(), String.valueOf(memberId)); } public String getToken(int memberId) { - return (String) redisTemplate.opsForHash().get(CacheKey.fcmTokenKey(), memberId); + return (String) redisTemplate.opsForHash().get(CacheKey.fcmTokenKey(), String.valueOf(memberId)); } } diff --git a/backend/src/main/java/com/worlabel/global/config/SecurityConfig.java b/backend/src/main/java/com/worlabel/global/config/SecurityConfig.java index 423e964..a2bb1fe 100644 --- a/backend/src/main/java/com/worlabel/global/config/SecurityConfig.java +++ b/backend/src/main/java/com/worlabel/global/config/SecurityConfig.java @@ -1,10 +1,8 @@ package com.worlabel.global.config; +import com.worlabel.domain.auth.handler.*; import com.worlabel.domain.auth.service.CustomOAuth2UserService; import com.worlabel.global.filter.JwtAuthenticationFilter; -import com.worlabel.domain.auth.handler.CustomAuthenticationDeniedHandler; -import com.worlabel.domain.auth.handler.CustomAuthenticationEntryPoint; -import com.worlabel.domain.auth.handler.OAuth2SuccessHandler; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -24,6 +22,7 @@ import java.util.List; @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { + private final CustomAuthenticationDeniedHandler authenticationDeniedHandler; private final CustomAuthenticationEntryPoint authenticationEntryPoint; private final JwtAuthenticationFilter jwtAuthenticationFilter; @@ -70,7 +69,6 @@ public class SecurityConfig { .requestMatchers("/swagger", "/swagger-ui.html", "/swagger-ui/**", "/api-docs", "/api-docs/**", "/v3/api-docs/**", "/ws/**").permitAll() .requestMatchers("/api/auth/reissue").permitAll() .anyRequest().authenticated() -// .anyRequest().permitAll() ); // OAuth2