JAVA/Spring Security

OAuth2 인증 및 JWT 관련 트러블슈팅 정리

min민 2025. 2. 20.

🔥 1. OAuth2 인증 시 네이버와 구글의 DefaultOidcUser 차이로 인한 문제

 

🛑 문제 상황

  • 네이버와 구글에서 받아오는 OAuth2 유저 객체가 다름.
  • 구글은 OIDC 기반이라 DefaultOidcUser를 반환하지만, 네이버는 OIDC 지원이 없어 DefaultOAuth2User를 반환함.
  • CustomOAuth2UserService에서 DefaultOidcUser로 캐스팅 시 예외 발생.

 

💡 해결 방법

  • OAuth2User 인터페이스를 기준으로 핸들링하는 팩토리 패턴을 적용.
  • OAuth2UserFactory를 만들어 제공자별 OAuth2Attribute 변환.

 

 

✅ OAuth2UserFactory 구현

@Component
@RequiredArgsConstructor
@Slf4j
public class OAuth2UserFactory {

    public OAuth2Attribute getOAuth2UserInfo(Authentication authentication) {
        if (authentication.getPrincipal() instanceof DefaultOidcUser) {
            return extractGoogleUserInfo((DefaultOidcUser) authentication.getPrincipal());
        } else if (authentication.getPrincipal() instanceof DefaultOAuth2User) {
            return extractGoogleUserInfo((DefaultOAuth2User) authentication.getPrincipal());
        } else if (authentication.getPrincipal() instanceof CustomOAuth2User) {
            return extractNaverUserInfo((CustomOAuth2User) authentication.getPrincipal());
        }
        log.error("Unexpected principal type: {}", authentication.getPrincipal().getClass().getName());
        throw new OAuth2AuthenticationException("Unsupported OAuth2 provider");
    }

    private OAuth2Attribute extractGoogleUserInfo(DefaultOAuth2User oauth2User) {
        Map<String, Object> attributes = oauth2User.getAttributes();

        return OAuth2Attribute.builder()
                .providerId((String) attributes.get("sub"))
                .email((String) attributes.get("email"))
                .nickname((String) attributes.get("name"))
                .provider("google")
                .build();
    }

    private OAuth2Attribute extractNaverUserInfo(CustomOAuth2User oAuth2User) {
        Map<String, Object> attributes = oAuth2User.getAttributes();
        Map<String, Object> response = (Map<String, Object>) attributes.get("response");

        return OAuth2Attribute.builder()
                .providerId((String) response.get("id"))
                .email((String) response.get("email"))
                .nickname((String) response.get("name"))
                .provider("naver")
                .build();
    }
}

 

 

 

 

🔥 2. JWT 필터에서 SecurityContextHolder 인증 정보가 설정되지 않는 문제

 

🛑 문제 상황

  • SecurityContextHolder.getContext().setAuthentication(authenticationToken); 호출 후에도 보안 컨텍스트가 설정되지 않음.
  • 인증되지 않은 요청으로 처리됨.

 

💡 해결 방법

  • doFilterInternal() 내부에서 SecurityContextHolder 설정 후 필터 체인 실행 확인.
  • UsernamePasswordAuthenticationToken 설정 시 setAuthenticated(true) 필요할 수도 있음.

 

✅ JWT 인증 필터 수정

if (jwtService.validateToken(token)) {
    UsernamePasswordAuthenticationToken authenticationToken =
        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    authenticationToken.setAuthenticated(true);  // ✅ 인증 설정 추가
    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}

 

 

 

🔥 3. OAuth2 인증 성공 후 Redirect 시 Header에 토큰이 포함되지 않는 문제

 

🛑 문제 상황

  • onAuthenticationSuccess 핸들러에서 response.sendRedirect(url); 호출 후 헤더에 토큰이 없음.
  • 리디렉션이 여러 번 발생하여 헤더에 토큰을 담아 전송하기 어려움.

 

💡 해결 방법

  • 쿠키를 이용하여 JWT 저장.
  • CORS 정책에서 allowCredentials(true) 설정 필요.

 

✅ OAuth2AuthenticationSuccessHandler 수정

jwtUtil.addTokenInCookie(response, "Authorization", tokens[0], 60 * 60);
jwtUtil.addTokenInCookie(response, "Refresh-Token", tokens[1], 60 * 60 * 24 * 7);

String redirectUrl = baseUrl + "/auth/callback";
getRedirectStrategy().sendRedirect(request, response, redirectUrl);

 

 

📌 Security & JWT 구현과정 트러블 슈팅 정리

  • OAuth2 인증 문제 해결: OAuth2UserFactory를 사용해 제공자별 유저 정보를 추출하도록 수정.
  • JWT 인증 필터 문제 해결: SecurityContextHolder 설정 후 setAuthenticated(true) 추가.
  • OAuth2 Redirect 문제 해결: 헤더가 아닌 쿠키를 이용해 JWT 저장.

'JAVA > Spring Security' 카테고리의 다른 글

Spring Security (스프링 시큐리티) Ch.1  (0) 2023.03.15