[WingITs / Spring Security] 인증은 됐는데 왜 역할은 ANONYMOUS?

Rose·2025년 5월 17일

개발 이슈 로그

목록 보기
1/2
post-thumbnail

프로젝트 소개

현재 학교 내 노트북 수리 요청 및 사용자 관리 시스템을 개발 중입니다.
Spring Boot와 Spring Security를 이용해 로컬 로그인과 카카오/구글 OAuth 로그인을 모두 지원합니다.

사용자 역할(Role)에 따라

  • 일반 학생(STUDENT)은 게시글 작성 및 수리 요청 가능
  • 관리자(MID_ADMIN, TOP_ADMIN)는 사용자/노트북 관리 권한을 갖습니다.

문제 상황

상단 메뉴바에 현재 로그인 중인 사용자 정보를 표시합니다. 이때 관리자 계정으로 로그인이 되어있기 때문에 TOP_ADMIN으로 표시되어야하나, 역할이 ANONYMOUS(손님)으로 잘못 표시되는 문제가 발생했습니다.

👉 문제는 게시글 상세 페이지(/posts/{id})에서만 발생했어요.
다른 페이지에서는 정상적으로 TOP_ADMIN | 관리자로 표시되었죠.

🔍 원인 분석

원인을 파악하기 위해 게시글 상세 페이지(/posts/{postId})와 관련된 컨트롤러에서 viewPost()메서드를 분석했습니다.

// 게시글 상세
    @GetMapping("/{postId}")
    public String viewPost(@PathVariable String postId,
                           Model model,
                           HttpServletRequest request) {

        postService.incrementViewCount(postId);
        Post post = postService.getPost(postId);
        model.addAttribute("post", post);
        model.addAttribute("comments", commentService.getCommentsByPostId(postId));

        // 현재 로그인한 사용자 정보를 가져오기 위한 인증 객체 획득
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        // 인증된 사용자의 경우
        if (authentication != null && authentication.isAuthenticated()) {
            Object principal = authentication.getPrincipal();

            if (principal instanceof CustomOAuth2User user) {
                model.addAttribute("currentUserEmail", user.getEmail());
                model.addAttribute("userRole", user.getRole().name());
            } else if (principal instanceof org.springframework.security.core.userdetails.User user) {
                // 로컬 유저의 경우 DB에서 이메일/역할 가져오기 (‼️문제 발생 의심 지점‼️)
                System.out.println("✅ 로컬사용자"); //출력 ❌
                String email = user.getUsername(); // email이 username에 담김
                String role = postService.findRoleByEmail(email); // PostService에서 사용자 role 조회하는 메서드 만들기

                model.addAttribute("currentUserEmail", email);
                model.addAttribute("userRole", role);
            } else if (principal instanceof org.springframework.security.core.userdetails.User user) {
                String email = user.getUsername();
                String role = postService.findRoleByEmail(email);
                model.addAttribute("currentUserEmail", email);
                model.addAttribute("userRole", role);

            } else if (principal instanceof String email) {
                model.addAttribute("currentUserEmail", email);
                model.addAttribute("userRole", "USER");

            } else {
                model.addAttribute("currentUserEmail", null);
                model.addAttribute("userRole", "ANONYMOUS");
            }
        } else {
            model.addAttribute("currentUserEmail", null);
            model.addAttribute("userRole", "ANONYMOUS");
        }

        // CSRF
        CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
        model.addAttribute("_csrf", csrfToken);

        return "post/view";
    }

콘솔 출력 결과 authentication != null && authentication.isAuthenticated()조건은 통과하지만 ✅ 로컬 사용자가 출력되지 않았습니다. principal instanceof org.springframework.security.core.userdetails.User user조건이 false가 되는 것이 원인이었죠.

SecurityContextHolder.getContext().getAuthentication().getPrincipal()을 통해 로그인 정보를 가져오는데, principal 객체 타입이 잘못되어 발생한 문제였습니다.

로그인 방식principal 타입
OAuth 로그인(구글, 카카오)CustomOAuth2User
로컬 로그인CustomUserDetails

👉 그런데 저는 컨트롤러에서 CustomOAuth2User만 체크하고, CustomUserDetails는 체크하지 않아
userRole = ANONYMOUS로 떨어졌던 겁니다.

✅ 해결 방법

viewPost() 메서드에서 principal의 타입을 명확히 분기했습니다.

if (principal instanceof CustomOAuth2User user) {
    // OAuth 사용자 처리
    ...
} else if (principal instanceof CustomUserDetails userDetails) {
    // 로컬 로그인 사용자 처리
    ...
}

이로써 로컬 로그인 사용자도 userRole, username이 정확하게 나왔답니다!!👏👏👏

배운 점

  • Spring Security의 Authentication.getPrincipal()은 로그인 방식에 따라 타입이 다르다.
  • 로그인 방식을 혼합해서 사용할 땐, @ControllerAdvice나 개별 컨트롤러에서 instanceof 분기 처리를 꼭 하자!
profile
개발자를 꿈꾸며, 하루하루 쌓아가는 로제의 지식 아카이브입니다.

0개의 댓글