hmkim 05-28
250528 김혜민 oauth2 일반회원 로그인 처리
@96e622e04f01747632fb07b7376bc02e4fadd381
src/main/java/com/takensoft/cms/mber/service/Impl/MberServiceImpl.java
--- src/main/java/com/takensoft/cms/mber/service/Impl/MberServiceImpl.java
+++ src/main/java/com/takensoft/cms/mber/service/Impl/MberServiceImpl.java
@@ -133,13 +133,12 @@
             if(joinDTO.getTelno() != null && !joinDTO.getTelno().equals("")) {
                 joinDTO.setTelno(Secret.encrypt(joinDTO.getTelno()));
             }
-
-            // 아이피 조회 및 등록
-            if (req != null) {
-                joinDTO.setFrstRegIp(httpRequestUtil.getIp(req));
-            } else {
-                joinDTO.setFrstRegIp("0.0.0.0"); // OAuth2의 경우 기본값
+            //멤버타입 없을시 default "S" 고정
+            if (joinDTO.getMbrType() == null || joinDTO.getMbrType().isEmpty()) {
+                joinDTO.setMbrType("S");
             }
+            // 아이피 조회 및 등록
+            joinDTO.setFrstRegIp(httpRequestUtil.getIp(req));
 
             // 등록된 토큰에서 사용자 정보 조회
             String writer = joinDTO.getRgtr();
@@ -344,13 +343,13 @@
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public MberVO saveOAuthUser(MberVO user) {
+    public MberVO saveOAuthUser(MberVO user, HttpServletRequest request) {
         try {
             // OAuth2 사용자를 JoinDTO로 변환하여 기존 검증된 로직 활용
             JoinDTO oauthJoinDTO = createOAuthJoinDTO(user);
 
             // 기존 userJoin 메서드 활용 (검증된 로직)
-            HashMap<String, Object> result = userJoin(null, oauthJoinDTO);
+            HashMap<String, Object> result = userJoin(request, oauthJoinDTO);
 
             // 생성된 회원 ID 설정
             user.setMbrId(result.get("mbrId").toString());
src/main/java/com/takensoft/cms/mber/service/MberService.java
--- src/main/java/com/takensoft/cms/mber/service/MberService.java
+++ src/main/java/com/takensoft/cms/mber/service/MberService.java
@@ -86,7 +86,7 @@
      *
      * OAuth2 사용자 저장
      */
-    public MberVO saveOAuthUser(MberVO user);
+    public MberVO saveOAuthUser(MberVO user, HttpServletRequest request);
 
     /**
      * @param user - OAuth2 사용자 정보
src/main/java/com/takensoft/common/config/SecurityConfig.java
--- src/main/java/com/takensoft/common/config/SecurityConfig.java
+++ src/main/java/com/takensoft/common/config/SecurityConfig.java
@@ -162,7 +162,7 @@
         );
 
         http.authorizeHttpRequests((auth) -> auth
-                .requestMatchers("/", "/mbr/**", "/refresh/**", "/sys/**", "/editFileUpload/**", "/fileUpload/**", "/oauth2/**", "/login/oauth2/**", "/.well-known/**").permitAll() // 회원, 토큰, 시스템 제공, 파일, OAuth2 접근 모두 허용
+                .requestMatchers("/", "/mbr/**", "/refresh/**", "/sys/**", "/editFileUpload/**", "/fileUpload/**", "/oauth2/**", "/login/oauth2/**").permitAll() // 회원, 토큰, 시스템 제공, 파일, OAuth2 접근 모두 허용
                 .requestMatchers("/admin/**").hasRole("ADMIN") // 관리자 페이지는 ADMIN 권한을 가진 사용자만 접근 가능
                 .anyRequest().authenticated() // 그 외에는 로그인한 사용자만 접근 가능
         );
src/main/java/com/takensoft/common/oauth/handler/OAuth2AuthenticationFailureHandler.java
--- src/main/java/com/takensoft/common/oauth/handler/OAuth2AuthenticationFailureHandler.java
+++ src/main/java/com/takensoft/common/oauth/handler/OAuth2AuthenticationFailureHandler.java
@@ -35,8 +35,6 @@
     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                         AuthenticationException exception) throws IOException, ServletException {
 
-        log.error("OAuth2 로그인 실패: {}", exception.getMessage());
-
         String errorMessage = mapErrorMessage(exception);
         String encodedMessage = URLEncoder.encode(errorMessage, StandardCharsets.UTF_8);
 
src/main/java/com/takensoft/common/oauth/handler/OAuth2AuthenticationSuccessHandler.java
--- src/main/java/com/takensoft/common/oauth/handler/OAuth2AuthenticationSuccessHandler.java
+++ src/main/java/com/takensoft/common/oauth/handler/OAuth2AuthenticationSuccessHandler.java
@@ -1,176 +1,247 @@
 package com.takensoft.common.oauth.handler;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.takensoft.cms.loginPolicy.service.LoginModeService;
 import com.takensoft.cms.loginPolicy.service.LoginPolicyService;
 import com.takensoft.cms.mber.service.LgnHstryService;
+import com.takensoft.cms.mber.service.MberService;
 import com.takensoft.cms.mber.vo.LgnHstryVO;
+import com.takensoft.cms.mber.vo.MberAuthorVO;
+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.oauth.vo.CustomOAuth2UserVO;
 import com.takensoft.common.util.HttpRequestUtil;
 import com.takensoft.common.util.JWTUtil;
 import com.takensoft.common.util.SessionUtil;
 import jakarta.servlet.ServletException;
+import jakarta.servlet.http.Cookie;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationContext;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
 import org.springframework.stereotype.Component;
 
 import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
-/**
- * @author takensoft
- * @since 2025.05.22
- * @modification
- *     since    |    author    | description
- *  2025.05.22  |  takensoft   | 최초 등록
- *
- * OAuth2 로그인 성공 핸들러
- */
 @Slf4j
 @Component
 @RequiredArgsConstructor
 public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
 
+    private final ApplicationContext applicationContext;
+    private final JWTUtil jwtUtil;
+    private final RefreshTokenService refreshTokenService;
     private final LgnHstryService lgnHstryService;
     private final HttpRequestUtil httpRequestUtil;
     private final LoginModeService loginModeService;
     private final LoginPolicyService loginPolicyService;
     private final SessionUtil sessionUtil;
-    private final JWTUtil jwtUtil;
     private final RedisTemplate<String, String> redisTemplate;
-    private final ApplicationEventPublisher eventPublisher;
 
     @Value("${jwt.accessTime}")
-    private long JWT_ACCESSTIME;
+    private long jwtAccessTime;
+
+    @Value("${jwt.refreshTime}")
+    private long jwtRefreshTime;
+
+    @Value("${cookie.time}")
+    private int cookieTime;
 
     @Value("${front.url}")
-    private String FRONT_URL;
+    private String frontUrl;
 
     @Override
-    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
-                                        Authentication authentication) throws IOException, ServletException {
+    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
 
         CustomOAuth2UserVO oAuth2User = (CustomOAuth2UserVO) authentication.getPrincipal();
 
         try {
-            log.info("OAuth2 로그인 성공 - Provider: {}, Email: {}",
-                    oAuth2User.getProvider(), oAuth2User.getEmail());
 
-            // 1. 비동기로 사용자 정보 저장/업데이트 처리
-            eventPublisher.publishEvent(new OAuth2UserSaveEvent(oAuth2User));
+            // MberService를 ApplicationContext에서 가져옴
+            MberService mberService = applicationContext.getBean(MberService.class);
 
-            // 2. 로그인 이력 저장
-            saveLoginHistory(request, oAuth2User);
+            // OAuth2 사용자 정보로 MberVO 생성 또는 조회
+            MberVO mber = processOAuth2User(oAuth2User, mberService, request);
 
-            // 3. 로그인 모드에 따른 토큰/세션 처리
+            // 로그인 이력 저장
+            saveLoginHistory(request, mber);
+
+            // 로그인 모드 확인
             String loginMode = loginModeService.getLoginMode();
-            String tempUserId = createTempUserId(oAuth2User);
-            processLoginByMode(request, response, oAuth2User, tempUserId, loginMode);
 
-            // 4. 캐시 방지 헤더 설정
-            response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
-            response.setHeader("Pragma", "no-cache");
-            response.setHeader("Expires", "0");
+            // 로그인 모드에 따른 처리
+            if ("S".equals(loginMode)) {
+                handleSessionMode(request, response, mber);
+            } else {
+                handleJwtMode(request, response, mber);
+            }
 
-            // 5. 프론트엔드 로그인 페이지로 리다이렉트 (OAuth 성공 파라미터 + 타임스탬프)
-            long timestamp = System.currentTimeMillis();
-            String redirectUrl = FRONT_URL + "/login.page?oauth_success=true&t=" + timestamp;
+            // 프론트엔드로 리다이렉트
+            String redirectUrl = frontUrl + "/login.page?oauth_success=true&loginMode=" + loginMode;
+
             getRedirectStrategy().sendRedirect(request, response, redirectUrl);
 
         } catch (Exception e) {
-            log.error("OAuth2 로그인 처리 중 오류 발생", e);
-            String errorUrl = FRONT_URL + "/login.page?error=oauth2_processing_failed";
-            getRedirectStrategy().sendRedirect(request, response, errorUrl);
+            e.printStackTrace();
+            handleOAuth2Error(response, e);
         }
     }
 
     /**
-     * 로그인 이력 저장
+     * JWT 모드 처리 - OAuth용 access token 쿠키 추가
      */
-    private void saveLoginHistory(HttpServletRequest request, CustomOAuth2UserVO oAuth2User) {
+    private void handleJwtMode(HttpServletRequest request, HttpServletResponse response, MberVO mber) throws IOException {
         try {
-            LgnHstryVO lgnHstryVO = new LgnHstryVO();
-            lgnHstryVO.setLgnId(oAuth2User.getEmail());
-            lgnHstryVO.setLgnType("1"); // 일반 사용자
-            lgnHstryVO.setCntnIp(httpRequestUtil.getIp(request));
-            lgnHstryVO.setCntnOperSys(httpRequestUtil.getOS(httpRequestUtil.getUserAgent(request)));
-            lgnHstryVO.setDeviceNm(httpRequestUtil.getDevice(httpRequestUtil.getUserAgent(request)));
-            lgnHstryVO.setBrwsrNm(httpRequestUtil.getBrowser(httpRequestUtil.getUserAgent(request)));
+            // JWT 토큰 생성
+            String accessToken = jwtUtil.createJwt("Authorization",
+                    mber.getMbrId(),
+                    mber.getLgnId(),
+                    mber.getMbrNm(),
+                    (List) mber.getAuthorities(),
+                    jwtAccessTime);
 
-            lgnHstryService.LgnHstrySave(lgnHstryVO);
+            String refreshToken = jwtUtil.createJwt("refresh",
+                    mber.getMbrId(),
+                    mber.getLgnId(),
+                    mber.getMbrNm(),
+                    (List) mber.getAuthorities(),
+                    jwtRefreshTime);
+            // Refresh 토큰 처리
+            RefreshTknVO refresh = new RefreshTknVO();
+            refresh.setMbrId(mber.getMbrId());
+
+            if (refreshTokenService.findByCheckRefresh(request, refresh)) {
+                refreshTokenService.delete(request, refresh);
+            }
+
+            refresh.setToken(refreshToken);
+
+            // 헤더와 쿠키 설정
+            response.setHeader("Authorization", accessToken);
+            // OAuth 전용 access token 쿠키 생성
+            Cookie oauthAccessCookie = new Cookie("oauth_access_token", accessToken);
+            oauthAccessCookie.setPath("/");
+            oauthAccessCookie.setMaxAge(300); // 5분 후 자동 삭제
+            oauthAccessCookie.setHttpOnly(false); // 프론트에서 접근 가능하도록
+
+            response.addCookie(oauthAccessCookie);
+
+            // Refresh 쿠키 생성
+            Cookie refreshCookie = jwtUtil.createCookie("refresh", refreshToken, cookieTime);
+            response.addCookie(refreshCookie);
+
+            response.setHeader("login-type", "J");
+
+            // 중복 로그인 비허용 처리
+            if (!loginPolicyService.getPolicy()) {
+                redisTemplate.delete("jwt:" + mber.getMbrId());
+                redisTemplate.opsForValue().set("jwt:" + mber.getMbrId(), accessToken, jwtAccessTime, TimeUnit.MILLISECONDS);
+            }
+
+            // Refresh 토큰 저장
+            refreshTokenService.saveRefreshToken(request, response, refresh, jwtRefreshTime);
+
         } catch (Exception e) {
-            log.error("OAuth2 로그인 이력 저장 실패", e);
+            e.printStackTrace();
+            throw e;
         }
-    }
-
-    /**
-     * 임시 사용자 ID 생성
-     */
-    private String createTempUserId(CustomOAuth2UserVO oAuth2User) {
-        return oAuth2User.getProvider() + "_" + oAuth2User.getId();
-    }
-
-    /**
-     * 로그인 모드에 따른 처리
-     */
-    private void processLoginByMode(HttpServletRequest request, HttpServletResponse response,
-                                    CustomOAuth2UserVO oAuth2User, String tempUserId, String loginMode)
-            throws IOException {
-
-        // JWT 토큰 생성
-        String accessToken = jwtUtil.createJwt(
-                "Authorization",
-                tempUserId,
-                oAuth2User.getEmail(),
-                oAuth2User.getName(),
-                null, // roles는 DB 저장 후 갱신 예정
-                JWT_ACCESSTIME
-        );
-
-        if ("S".equals(loginMode)) {
-            // 세션 모드 처리
-            processSessionMode(request, tempUserId, accessToken, oAuth2User);
-        } else {
-            // JWT 모드 처리 (기본값)
-            processJwtMode(response, tempUserId, accessToken);
-        }
-
-        response.setHeader("login-type", loginMode);
     }
 
     /**
      * 세션 모드 처리
      */
-    private void processSessionMode(HttpServletRequest request, String tempUserId,
-                                    String accessToken, CustomOAuth2UserVO oAuth2User) {
-        HttpSession session = request.getSession(true);
-        session.setAttribute("JWT_TOKEN", accessToken);
-        session.setAttribute("oauth2User", oAuth2User);
+    private void handleSessionMode(HttpServletRequest request, HttpServletResponse response, MberVO mber) {
+        log.info("세션 모드로 OAuth2 로그인 처리");
 
-        // 중복 로그인 정책 확인
+        // 세션 생성 및 정보 저장
+        HttpSession session = request.getSession(true);
+
+        // 세션에 사용자 정보 저장 (JWT 없이!)
+        session.setAttribute("mbrId", mber.getMbrId());
+        session.setAttribute("mbrNm", mber.getMbrNm());
+        session.setAttribute("lgnId", mber.getLgnId());
+        session.setAttribute("roles", mber.getAuthorList());
+        session.setAttribute("loginType", "OAUTH2");
+
+        // 중복 로그인 비허용 처리
         if (!loginPolicyService.getPolicy()) {
-            sessionUtil.registerSession(tempUserId, session);
+            sessionUtil.registerSession(mber.getMbrId(), session);
+        }
+
+        response.setHeader("login-type", "S");
+    }
+
+    /**
+     * 로그인 이력 저장
+     */
+    private void saveLoginHistory(HttpServletRequest request, MberVO mber) {
+        try {
+            String userAgent = httpRequestUtil.getUserAgent(request);
+
+            LgnHstryVO loginHistory = new LgnHstryVO();
+            loginHistory.setLgnId(mber.getLgnId());
+            loginHistory.setLgnType(mber.getAuthorities().stream()
+                    .anyMatch(r -> r.getAuthority().equals("ROLE_ADMIN")) ? "0" : "1");
+            loginHistory.setCntnIp(httpRequestUtil.getIp(request));
+            loginHistory.setCntnOperSys(httpRequestUtil.getOS(userAgent));
+            loginHistory.setDeviceNm(httpRequestUtil.getDevice(userAgent));
+            loginHistory.setBrwsrNm(httpRequestUtil.getBrowser(userAgent));
+
+            lgnHstryService.LgnHstrySave(loginHistory);
+        } catch (Exception e) {
+            log.error("로그인 이력 저장 실패", e);
+            // 로그인 이력 저장 실패해도 로그인은 계속 진행
         }
     }
 
     /**
-     * JWT 모드 처리
+     * OAuth2 사용자 처리
      */
-    private void processJwtMode(HttpServletResponse response, String tempUserId, String accessToken) {
-        response.setHeader("Authorization", accessToken);
+    private MberVO processOAuth2User(CustomOAuth2UserVO oAuth2User, MberService mberService, HttpServletRequest request) throws JsonProcessingException {
+        String mbrType = convertProviderToMbrType(oAuth2User.getProvider());
+        MberVO existingUser = mberService.findByEmailAndProvider(oAuth2User.getEmail(), mbrType);
+        if (existingUser != null) {
+            existingUser.setMbrNm(oAuth2User.getName());
+            return mberService.updateOAuthUser(existingUser);
+        } else {
+            MberVO newUser = new MberVO();
+            newUser.setEml(oAuth2User.getEmail());
+            newUser.setLgnId(oAuth2User.getEmail().toLowerCase());
+            newUser.setMbrNm(oAuth2User.getName());
+            newUser.setNcnm(oAuth2User.getName());
+            newUser.setMbrType(mbrType);
+            MberAuthorVO roleUser = new MberAuthorVO();
+            roleUser.setAuthrtCd("ROLE_USER");
+            newUser.setAuthorList(Collections.singletonList(roleUser));
 
-        // 중복 로그인 정책 확인
-        if (!loginPolicyService.getPolicy()) {
-            redisTemplate.delete("jwt:" + tempUserId);
-            redisTemplate.opsForValue().set("jwt:" + tempUserId, accessToken,
-                    JWT_ACCESSTIME, TimeUnit.MILLISECONDS);
+            return mberService.saveOAuthUser(newUser, request);
         }
     }
+
+    private String convertProviderToMbrType(String provider) {
+        return switch (provider.toLowerCase()) {
+            case "kakao" -> "K";
+            case "naver" -> "N";
+            case "google" -> "G";
+            case "facebook" -> "F";
+            default -> "S";
+        };
+    }
+
+    private void handleOAuth2Error(HttpServletResponse response, Exception e) throws IOException {
+        String message = URLEncoder.encode("OAuth 로그인에 실패했습니다.", "UTF-8");
+        String errorUrl = String.format("%s/login.page?error=oauth2_failed&message=%s", frontUrl, message);
+        getRedirectStrategy().sendRedirect(null, response, errorUrl);
+    }
 }
(파일 끝에 줄바꿈 문자 없음)
 
src/main/java/com/takensoft/common/oauth/handler/OAuth2UserSaveEvent.java (deleted)
--- src/main/java/com/takensoft/common/oauth/handler/OAuth2UserSaveEvent.java
@@ -1,25 +0,0 @@
-package com.takensoft.common.oauth.handler;
-
-import com.takensoft.common.oauth.vo.CustomOAuth2UserVO;
-import lombok.Getter;
-import org.springframework.context.ApplicationEvent;
-
-/**
- * @author takensoft
- * @since 2025.05.22
- * @modification
- *     since    |    author    | description
- *  2025.05.22  |  takensoft   | 최초 등록
- *
- * OAuth2 사용자 저장 이벤트
- */
-@Getter
-public class OAuth2UserSaveEvent extends ApplicationEvent {
-
-    private final CustomOAuth2UserVO oAuth2User;
-
-    public OAuth2UserSaveEvent(CustomOAuth2UserVO oAuth2User) {
-        super(oAuth2User);
-        this.oAuth2User = oAuth2User;
-    }
-}(파일 끝에 줄바꿈 문자 없음)
 
src/main/java/com/takensoft/common/oauth/handler/OAuth2UserSaveEventListener.java (deleted)
--- src/main/java/com/takensoft/common/oauth/handler/OAuth2UserSaveEventListener.java
@@ -1,119 +0,0 @@
-package com.takensoft.common.oauth.handler;
-
-import com.takensoft.cms.mber.service.MberService;
-import com.takensoft.cms.mber.vo.MberAuthorVO;
-import com.takensoft.cms.mber.vo.MberVO;
-import com.takensoft.common.oauth.vo.CustomOAuth2UserVO;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.event.EventListener;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Component;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author takensoft
- * @since 2025.05.22
- * @modification
- *     since    |    author    | description
- *  2025.05.22  |  takensoft   | 최초 등록
- *
- * OAuth2 사용자 저장 이벤트 리스너
- */
-@Component
-@RequiredArgsConstructor
-@Slf4j
-public class OAuth2UserSaveEventListener {
-
-    private final MberService mberService;
-
-    @EventListener
-    @Async
-    public void handleOAuth2UserSaveEvent(OAuth2UserSaveEvent event) {
-        try {
-            CustomOAuth2UserVO oAuth2User = event.getOAuth2User();
-            processOAuth2User(oAuth2User);
-        } catch (Exception e) {
-            log.error("OAuth2 사용자 저장 이벤트 처리 실패", e);
-        }
-    }
-
-    /**
-     * OAuth2 사용자 처리 (저장 또는 업데이트)
-     */
-    private void processOAuth2User(CustomOAuth2UserVO oAuth2User) {
-        String mbrType = convertProviderToMbrType(oAuth2User.getProvider());
-
-        // 이메일과 제공자로 기존 사용자 조회
-        MberVO existingUser = mberService.findByEmailAndProvider(oAuth2User.getEmail(), mbrType);
-
-        if (existingUser != null) {
-            updateExistingUser(existingUser, oAuth2User);
-        } else {
-            createNewUser(oAuth2User, mbrType);
-        }
-    }
-
-    /**
-     * 기존 사용자 정보 업데이트
-     */
-    private void updateExistingUser(MberVO existingUser, CustomOAuth2UserVO oAuth2User) {
-        try {
-            existingUser.setMbrNm(oAuth2User.getName());
-            // 프로필 이미지 URL이 있다면 업데이트
-            if (oAuth2User.getImageUrl() != null) {
-                // 필요시 프로필 이미지 필드 추가
-            }
-
-            mberService.updateOAuthUser(existingUser);
-            log.info("기존 OAuth2 사용자 정보 업데이트 완료: {}", existingUser.getEml());
-        } catch (Exception e) {
-            log.error("기존 OAuth2 사용자 업데이트 실패: {}", oAuth2User.getEmail(), e);
-        }
-    }
-
-    /**
-     * 새 OAuth2 사용자 생성
-     */
-    private void createNewUser(CustomOAuth2UserVO oAuth2User, String mbrType) {
-        try {
-            MberVO newUser = new MberVO();
-            newUser.setEml(oAuth2User.getEmail());
-            newUser.setMbrNm(oAuth2User.getName());
-            newUser.setNcnm(oAuth2User.getName());
-            newUser.setMbrType(mbrType);
-            newUser.setAuthorList(createDefaultAuthorities());
-
-            mberService.saveOAuthUser(newUser);
-            log.info("새 OAuth2 사용자 생성 완료: {}", newUser.getEml());
-        } catch (Exception e) {
-            log.error("새 OAuth2 사용자 생성 실패: {}", oAuth2User.getEmail(), e);
-        }
-    }
-
-    /**
-     * 기본 권한 생성
-     */
-    private List<MberAuthorVO> createDefaultAuthorities() {
-        List<MberAuthorVO> authorities = new ArrayList<>();
-        MberAuthorVO userRole = new MberAuthorVO();
-        userRole.setAuthrtCd("ROLE_USER");
-        authorities.add(userRole);
-        return authorities;
-    }
-
-    /**
-     * OAuth2 제공자를 회원 타입으로 변환
-     */
-    private String convertProviderToMbrType(String provider) {
-        return switch (provider.toLowerCase()) {
-            case "kakao" -> "K";
-            case "naver" -> "N";
-            case "google" -> "G";
-            case "facebook" -> "F";
-            default -> "S"; // 일반 회원
-        };
-    }
-}(파일 끝에 줄바꿈 문자 없음)
src/main/java/com/takensoft/common/oauth/service/Impl/CustomOAuth2UserServiceImpl.java
--- src/main/java/com/takensoft/common/oauth/service/Impl/CustomOAuth2UserServiceImpl.java
+++ src/main/java/com/takensoft/common/oauth/service/Impl/CustomOAuth2UserServiceImpl.java
@@ -29,8 +29,7 @@
 @Service("customOAuth2UserService")
 @RequiredArgsConstructor
 @Slf4j
-public class CustomOAuth2UserServiceImpl extends EgovAbstractServiceImpl
-        implements CustomOAuth2UserService, OAuth2UserService<OAuth2UserRequest, OAuth2User> {
+public class CustomOAuth2UserServiceImpl extends EgovAbstractServiceImpl implements CustomOAuth2UserService, OAuth2UserService<OAuth2UserRequest, OAuth2User> {
 
     private final DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
 
@@ -41,7 +40,7 @@
      * @throws DataAccessException - db 관련 예외 발생 시
      * @throws Exception - 그 외 예외 발생 시
      *
-     * OAuth2 사용자 정보 로드 (최소 처리)
+     * OAuth2 사용자 정보 로드
      */
     @Override
     public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
@@ -49,10 +48,8 @@
             OAuth2User oAuth2User = delegate.loadUser(userRequest);
             return processOAuth2User(userRequest, oAuth2User);
         } catch (DataAccessException dae) {
-            log.error("OAuth2 사용자 처리 중 DB 오류 발생", dae);
             throw new OAuth2AuthenticationException("OAuth2 사용자 처리 실패 - DB 오류");
         } catch (Exception e) {
-            log.error("OAuth2 사용자 처리 중 오류 발생", e);
             throw new OAuth2AuthenticationException("OAuth2 사용자 처리 실패");
         }
     }
@@ -64,7 +61,7 @@
      * @throws DataAccessException - db 관련 예외 발생 시
      * @throws Exception - 그 외 예외 발생 시
      *
-     * OAuth2 사용자 정보 처리 (단순히 CustomOAuth2User 객체만 생성)
+     * OAuth2 사용자 정보 처리
      */
     private OAuth2User processOAuth2User(OAuth2UserRequest userRequest, OAuth2User oAuth2User) {
         try {
src/main/java/com/takensoft/common/oauth/web/OAuth2Controller.java
--- src/main/java/com/takensoft/common/oauth/web/OAuth2Controller.java
+++ src/main/java/com/takensoft/common/oauth/web/OAuth2Controller.java
@@ -1,11 +1,14 @@
 package com.takensoft.common.oauth.web;
 
+import com.takensoft.cms.loginPolicy.service.LoginModeService;
 import com.takensoft.cms.mber.service.MberService;
 import com.takensoft.cms.mber.vo.MberVO;
 import com.takensoft.common.message.MessageCode;
 import com.takensoft.common.service.VerificationService;
 import com.takensoft.common.util.HttpRequestUtil;
+import com.takensoft.common.util.JWTUtil;
 import com.takensoft.common.util.ResponseUtil;
+import jakarta.servlet.http.Cookie;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
@@ -13,16 +16,13 @@
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author takensoft
@@ -31,6 +31,7 @@
  *     since    |    author    | description
  *  2025.05.22  |  takensoft   | 최초 등록
  *  2025.05.26  |  takensoft   | OAuth 리다이렉트 기능 추가
+ *  2025.05.28  |  takensoft   | 쿠키에서 OAuth 토큰 읽기 추가
  *
  * OAuth2 관련 통합 컨트롤러
  */
@@ -44,6 +45,8 @@
     private final VerificationService verificationService;
     private final ResponseUtil resUtil;
     private final HttpRequestUtil httpRequestUtil;
+    private final LoginModeService loginModeService;
+    private final JWTUtil jwtUtil;
 
     @Value("${front.url}")
     private String FRONT_URL;
@@ -56,17 +59,10 @@
      * 프론트엔드에서 provider 정보를 받아 검증 후 OAuth2 서버로 리다이렉트
      */
     @GetMapping("/login")
-    public void redirectToOAuth(@RequestParam String provider,
-                                HttpServletRequest request,
-                                HttpServletResponse response) throws IOException {
+    public void redirectToOAuth(@RequestParam String provider, HttpServletRequest request, HttpServletResponse response) throws IOException {
 
         String clientIP = httpRequestUtil.getIp(request);
         String userAgent = httpRequestUtil.getUserAgent(request);
-
-        log.info("=== OAuth 로그인 시도 ===");
-        log.info("Provider: {}", provider);
-        log.info("Client IP: {}", clientIP);
-        log.info("User Agent: {}", userAgent);
 
         try {
             // 1. Provider 유효성 검증
@@ -83,20 +79,15 @@
 
             // 4. OAuth2 Authorization Server로 리다이렉트
             String redirectUrl = "/oauth2/authorization/" + provider;
-            log.info("OAuth 리다이렉트 URL: {}", redirectUrl);
 
-            // 리다이렉트 상태 코드를 명시적으로 설정
             response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
             response.sendRedirect(redirectUrl);
 
         } catch (IllegalArgumentException e) {
-            log.error("OAuth 로그인 실패 - 잘못된 Provider: {}", provider);
             handleError(response, "invalid_provider", e.getMessage());
         } catch (SecurityException e) {
-            log.error("OAuth 로그인 실패 - 보안 검증 실패: {}", e.getMessage());
             handleError(response, "security_check_failed", e.getMessage());
         } catch (Exception e) {
-            log.error("OAuth 로그인 실패 - 시스템 오류", e);
             handleError(response, "system_error", "OAuth 로그인 중 오류가 발생했습니다.");
         }
     }
@@ -104,39 +95,107 @@
     /**
      * OAuth2 로그인 후 사용자 정보 조회
      */
-    @GetMapping(value = "/user-info")
+    @PostMapping(value = "/getUserInfo.json")
     public ResponseEntity<?> getUserInfo(HttpServletRequest request) {
         try {
-            // 현재 로그인된 사용자 ID 추출
-            String currentUserId = verificationService.getCurrentUserId();
+            // 로그인 모드 확인
+            String loginMode = loginModeService.getLoginMode();
 
-            if (currentUserId == null || currentUserId.isEmpty()) {
-                // 세션에서 OAuth2 정보 확인 (세션 모드인 경우)
+            // 현재 로그인한 사용자 ID 조회
+            String currentUserId = null;
+            MberVO mberInfo = null;
+
+            if ("S".equals(loginMode)) {
+                // 세션 모드 - 세션에서 직접 조회
                 HttpSession session = request.getSession(false);
-                if (session != null && session.getAttribute("oauth2User") != null) {
-                    return handleSessionOAuth2User(session);
+                if (session != null) {
+                    currentUserId = (String) session.getAttribute("mbrId");
+
+                    if (currentUserId != null) {
+                        // 세션에 저장된 정보로 응답 생성
+                        HashMap<String, Object> result = new HashMap<>();
+                        result.put("mbrId", session.getAttribute("mbrId"));
+                        result.put("mbrNm", session.getAttribute("mbrNm"));
+                        result.put("roles", session.getAttribute("roles"));
+
+                        return resUtil.successRes(result, MessageCode.COMMON_SUCCESS);
+                    }
+                } else {
+                    return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
+                }
+            } else {
+                // JWT 모드 - 토큰에서 조회
+                String authHeader = request.getHeader("Authorization");
+                System.out.println("Authorization Header: " + authHeader);
+
+                String token = null;
+
+                if (authHeader != null && !authHeader.isEmpty()) {
+                    // Authorization 헤더에서 토큰 추출
+                    token = jwtUtil.extractToken(authHeader);
+                } else {
+                    // Authorization 헤더가 없으면 OAuth 전용 쿠키에서 확인
+                    token = getTokenFromOAuthCookie(request);
                 }
 
-                return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
+                if (token == null || token.isEmpty()) {
+                    return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
+                }
+
+                try {
+                    currentUserId = (String) jwtUtil.getClaim(token, "mbrId");
+
+                    // JWT 모드에서는 DB에서 최신 정보 조회
+                    HashMap<String, Object> params = new HashMap<>();
+                    params.put("mbrId", currentUserId);
+                    mberInfo = mberService.findByMbr(params);
+
+                    if (mberInfo == null) {
+                        return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
+                    }
+
+                    // 응답 데이터 구성
+                    HashMap<String, Object> result = new HashMap<>();
+                    result.put("mbrId", mberInfo.getMbrId());
+                    result.put("mbrNm", mberInfo.getMbrNm());
+                    result.put("roles", mberInfo.getAuthorList());
+
+                    // 토큰도 함께 전달
+                    if (authHeader != null && !authHeader.isEmpty()) {
+                        result.put("token", authHeader);
+                    } else {
+                        // 쿠키에서 가져온 토큰이면 Bearer 형태로 반환
+                        String oauthToken = getTokenFromOAuthCookie(request);
+                        if (oauthToken != null) {
+                            result.put("token", "Bearer " + oauthToken);
+                        }
+                    }
+                    return resUtil.successRes(result, MessageCode.COMMON_SUCCESS);
+
+                } catch (IllegalArgumentException e) {
+                    return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
+                }
             }
-
-            // DB에서 사용자 정보 조회
-            HashMap<String, Object> params = new HashMap<>();
-            params.put("mbrId", currentUserId);
-            MberVO userInfo = mberService.findByMbr(params);
-
-            if (userInfo == null) {
-                return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
-            }
-
-            // 응답 데이터 구성
-            HashMap<String, Object> response = createUserResponse(userInfo);
-            return resUtil.successRes(response, MessageCode.COMMON_SUCCESS);
+            // 여기까지 왔다면 사용자를 찾을 수 없음
+            return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
 
         } catch (Exception e) {
-            log.error("사용자 정보 조회 실패", e);
             return resUtil.errorRes(MessageCode.COMMON_UNKNOWN_ERROR);
         }
+    }
+
+    /**
+     * OAuth 전용 쿠키에서 access token 추출
+     */
+    private String getTokenFromOAuthCookie(HttpServletRequest request) {
+        if (request.getCookies() != null) {
+            for (Cookie cookie : request.getCookies()) {
+                if ("oauth_access_token".equals(cookie.getName())) {
+                    return cookie.getValue();
+                }
+            }
+        }
+        return null;
     }
 
     /**
@@ -144,7 +203,6 @@
      */
     @GetMapping("/providers")
     public ResponseEntity<?> getSupportedProviders() {
-        log.info("지원하는 OAuth 제공자 목록 조회");
         return resUtil.successRes(SUPPORTED_PROVIDERS, MessageCode.COMMON_SUCCESS);
     }
 
@@ -159,22 +217,13 @@
         if (!SUPPORTED_PROVIDERS.contains(provider.toLowerCase())) {
             throw new IllegalArgumentException("지원하지 않는 OAuth 제공자입니다: " + provider);
         }
-
-        // TODO: 동적 설정으로 특정 제공자 비활성화 체크
-        // if (!oauthConfigService.isProviderEnabled(provider)) {
-        //     throw new IllegalArgumentException(provider + " 로그인이 일시 중단되었습니다.");
-        // }
     }
 
     /**
      * 보안 검증 (필요시 확장)
      */
     private void validateSecurity(HttpServletRequest request) {
-        // TODO: 필요시 보안 검증 로직 추가
-        // 예: IP 화이트리스트, Rate Limiting, 사용자 상태 체크 등
-
         String clientIP = httpRequestUtil.getIp(request);
-
         // 예시: 로컬 개발 환경이 아닌 경우 추가 검증
         if (!"127.0.0.1".equals(clientIP) && !"::1".equals(clientIP)) {
             // 운영 환경 보안 검증 로직
@@ -186,50 +235,7 @@
      */
     private void handleError(HttpServletResponse response, String errorCode, String errorMessage) throws IOException {
         String encodedMessage = java.net.URLEncoder.encode(errorMessage, "UTF-8");
-        String errorUrl = String.format("%s/login?error=%s&message=%s", FRONT_URL, errorCode, encodedMessage);
-
-        log.info("에러 리다이렉트 URL: {}", errorUrl);
+        String errorUrl = String.format("%s/login.page?error=%s&message=%s", FRONT_URL, errorCode, encodedMessage);
         response.sendRedirect(errorUrl);
-    }
-
-    /**
-     * 세션의 OAuth2 사용자 정보 처리
-     */
-    private ResponseEntity<?> handleSessionOAuth2User(HttpSession session) {
-        try {
-            // 세션에서 OAuth2 사용자 정보 추출
-            // 이는 DB 저장이 완료되기 전의 임시 상태
-            HashMap<String, Object> tempResponse = new HashMap<>();
-            tempResponse.put("mbrId", "TEMP_OAUTH2");
-            tempResponse.put("mbrNm", "OAuth2 User");
-            tempResponse.put("roles", new String[]{"ROLE_USER"});
-            tempResponse.put("isTemporary", true);
-
-            return resUtil.successRes(tempResponse, MessageCode.COMMON_SUCCESS);
-        } catch (Exception e) {
-            log.error("세션 OAuth2 사용자 정보 처리 실패", e);
-            return resUtil.errorRes(MessageCode.COMMON_UNKNOWN_ERROR);
-        }
-    }
-
-    /**
-     * 사용자 응답 데이터 생성
-     */
-    private HashMap<String, Object> createUserResponse(MberVO userInfo) {
-        HashMap<String, Object> response = new HashMap<>();
-        response.put("mbrId", userInfo.getMbrId());
-        response.put("mbrNm", userInfo.getMbrNm());
-        response.put("eml", userInfo.getEml());
-        response.put("ncnm", userInfo.getNcnm());
-        response.put("mbrType", userInfo.getMbrType());
-
-        // 권한 정보 변환
-        String[] roles = userInfo.getAuthorList().stream()
-                .map(auth -> auth.getAuthrtCd())
-                .toArray(String[]::new);
-        response.put("roles", roles);
-        response.put("isTemporary", false);
-
-        return response;
     }
 }
(파일 끝에 줄바꿈 문자 없음)
src/main/java/com/takensoft/common/util/JWTUtil.java
--- src/main/java/com/takensoft/common/util/JWTUtil.java
+++ src/main/java/com/takensoft/common/util/JWTUtil.java
@@ -81,16 +81,6 @@
         cookie.setHttpOnly(true); // front에서 script로 접근 방지
         return cookie;
     }
-    // 로그인 사용자 아이디 조회
-    public String getWriter() {
-        String mbrId = null;
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        if(authentication != null && authentication.isAuthenticated()) {
-            Object principal = authentication.getPrincipal();
-            if(principal instanceof UserDetails) mbrId = ((MberVO) authentication.getPrincipal()).getMbrId();
-        }
-        return mbrId;
-    }
 
     /**
      * @param tkn JWT 토큰 문자열
@@ -103,6 +93,13 @@
     public Object getClaim(String tkn, String knd) {
         Claims claims;
         try {
+            // 토큰 값 검증
+            if (tkn == null || tkn.trim().isEmpty()) {
+                throw new IllegalArgumentException("Token is null or empty");
+            }else{
+                tkn = extractToken(tkn);
+            }
+
             claims = Jwts.parser()
                     .verifyWith(JWT_SECRET_KEY)
                     .build()
@@ -144,4 +141,17 @@
         }
     }
 
+    /**
+     * @param authHeader JWT 토큰 문자열
+     * @return 실제 토큰 부분만 추출
+     *
+     * Bearer 토큰에서 실제 토큰 부분만 추출하는 메서드
+     */
+    public String extractToken(String authHeader) {
+        if (authHeader != null && authHeader.startsWith("Bearer ")) {
+            return authHeader.substring(7);
+        }
+        return authHeader; // Bearer가 없으면 그대로 반환
+    }
+
 }
src/main/resources/mybatis/mapper/mber/mber-SQL.xml
--- src/main/resources/mybatis/mapper/mber/mber-SQL.xml
+++ src/main/resources/mybatis/mapper/mber/mber-SQL.xml
@@ -149,7 +149,7 @@
             , #{smsRcptnAgreYn}
             , #{emlRcptnAgreYn}
             , #{prvcRlsYn}
-            , 'S'
+            , #{mbrType}
             , NOW()
             , #{frstRegIp}
             , '1'
Add a comment
List