package com.takensoft.cms.token.web; import com.takensoft.cms.loginPolicy.service.LoginModeService; import com.takensoft.cms.loginPolicy.service.LoginPolicyService; import com.takensoft.cms.mber.vo.MberVO; import com.takensoft.cms.token.service.RefreshTokenService; import com.takensoft.cms.token.vo.RefreshTknVO; import com.takensoft.common.message.MessageCode; import com.takensoft.common.util.ResponseData; import com.takensoft.common.util.ResponseUtil; import com.takensoft.common.util.SessionUtil; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.nio.charset.Charset; /** * @author takensoft * @since 2024.04.01 * @modification * since | author | description * 2024.04.01 | takensoft | 최초 등록 * 2025.06.02 | takensoft | 세션 모드 Redis 정보 삭제 추가 * * RefreshToken 정보 관련 컨트롤러 */ @RestController @RequiredArgsConstructor @Slf4j public class RefreshTokenController { private final ResponseUtil resUtil; private final RefreshTokenService refreshTokenService; private final LoginPolicyService loginPolicyService; private final LoginModeService loginModeService; private final SessionUtil sessionUtil; private final RedisTemplate redisTemplate; /** * @param req - HTTP 요청 객체 * @param res - HTTP 응답 객체 * @return ResponseEntity - 로그아웃 응답 결과 * * 로그아웃 - 세션/JWT 모드 통합 처리 */ @PostMapping(value = "/mbr/logout.json") public ResponseEntity logout(HttpServletRequest req, HttpServletResponse res){ Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getPrincipal() instanceof MberVO) { MberVO mber = (MberVO) auth.getPrincipal(); String mbrId = mber.getMbrId(); String loginMode = loginModeService.getLoginMode(); // J or S // Refresh 토큰 삭제 (DB) RefreshTknVO refresh = new RefreshTknVO(); refresh.setMbrId(mbrId); int result = refreshTokenService.delete(req, refresh); if (loginMode.equals("S")) { HttpSession session = req.getSession(false); if (session != null) { session.invalidate(); } // SessionUtil에서 제거 sessionUtil.removeSession(mbrId); // Redis에서 세션 정보 삭제 (중복로그인 관리용) String sessionKey = "session:" + mbrId; try { redisTemplate.delete(sessionKey); } catch (Exception e) { } // JSESSIONID 쿠키 제거 Cookie cookie = new Cookie("JSESSIONID", null); cookie.setMaxAge(0); // 삭제 cookie.setPath("/"); res.addCookie(cookie); } else { // JWT 방식: Redis에서 삭제 if (!loginPolicyService.getPolicy()) { try { redisTemplate.delete("jwt:" + mbrId); } catch (Exception e) { } } // refresh 쿠키 제거 Cookie cookie = new Cookie("refresh", null); cookie.setMaxAge(0); cookie.setHttpOnly(true); cookie.setPath("/"); res.addCookie(cookie); } // SecurityContext 제거 SecurityContextHolder.clearContext(); return resUtil.successRes(result, MessageCode.LOGOUT_SUCCESS); } return resUtil.errorRes(MessageCode.COMMON_UNKNOWN_ERROR); } /** * @param req - HTTP 요청 객체 * @param res - HTTP 응답 객체 * @return ResponseEntity - 토큰 재발급 응답 결과 * * 토큰 재발급 */ @PostMapping("/refresh/tokenReissue.json") public ResponseEntity tokenReissue(HttpServletRequest req, HttpServletResponse res) { int result = refreshTokenService.tokenReissueProc(req, res); // 응답 처리 HttpHeaders headers = new HttpHeaders(); headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8"))); ResponseData responseData = new ResponseData(); if(result > 0) { return resUtil.successRes(result, MessageCode.COMMON_SUCCESS); } else { responseData.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); responseData.setStatusText(HttpStatus.INTERNAL_SERVER_ERROR); responseData.setMessage("로그인을 다시해주시기 바랍니다."); return new ResponseEntity<>(responseData, headers, HttpStatus.INTERNAL_SERVER_ERROR); } } }