본문 바로가기

Proj/Tripot

Trouble Shooting: 무한 리다이렉트 현상

1. 개요

해당 프로젝트에서 소셜 로그인 파트를 맡고 개발을 하던 중이었다. 

카카오 로그인 후 같은 페이지를 계속 리디렉션하는 현상이 발생하여 이를 해결하고자 하는 과정이다.

2. 해결

로깅 레벨을 debug로 바꾸고 이를 확인해보았다. 위 사진 이후 OAuth2UserService.loadUser가 실행되고, 다시 /로 리다이렉트되는 것을 확인할 수 있었다.

2-1. uri 권한 확인

위 사진에서 다음의 순서대로 동작하는 것을 확인할 수 있었다.

  1. /oauth2/authorization/kakao로 리다이렉트
  2. kauth.kakao.com/oauth/authorize으로 리다이렉트를 걸어서 인가 코드를 받아옴
  3. kauth.kakao.com/oauth/token으로 가서 토큰을 받아옴
  4. kapi.kakao.com/v2/user/me로 이동하여 유저 정보를 받아옴

그런데 처음에 SecurityConfig에서 설정했던 유저 정보는 다음과 같았다.

이렇게 되면 1번 과정부터 인증을 시작해야 되는데 이미 인증이 된 상태이므로 접근이 불가했다. 따라서 이에 관한 코드를 추가했다.

이러고 돌려봤지만 아직 해결되지 않았다.

 

2-2. 디버깅

sendRedirect 함수이다. 해당 위치에서 url이 "/"로 설정되는 것을 확인할 수 있었다.

AbstractAuthenticationTargtUrlRequestHandler.handle이다. 여기서 리다이렉트를 걸었다는 걸 확인할 수 있었다.

 

참고로 디버깅 중 breakpoint를 걸고 step out하면 해당 함수를 호출한 함수를 확인할 수 있다.

handle을 부른 함수는 SimpleUrlAuthenticationSuccessHandler.onAuthenticationSuccess이다. 그런데 이상하다. 필자가 정의했던 로그인 성공 메서드는 다음과 같다.

@Component
@RequiredArgsConstructor
@Slf4j
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private final JwtUtil jwtUtil;
    private final ObjectMapper objectMapper;
    private final RedisUtil redisUtil;

    //oauth 로그인 성공 시 로직
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {

        //인증된 사용자 정보 가져오기
        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
        log.info("[{}} 사용자 정보 로딩: {}", getClass().getName(), principal);

        Member member = principal.getMember();
        String username = principal.getUsername();

        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
        GrantedAuthority auth = iterator.next();
        String role = auth.getAuthority();

        //JWT 생성
        LoginCreateJwtDto loginCreateJwtDto = LoginCreateJwtDto.builder()
                .id(member.getId())
                .username(username)
                .role(role)
                .requestTimeMs(LocalDateTime.now())
                .build();

        String accessToken = jwtUtil.createJwt(loginCreateJwtDto, "access", 60 * 60 * 60L);
        String refreshToken = jwtUtil.createJwt(loginCreateJwtDto, "refresh", 8640_0000L);
        log.info("[{}} JWT 토큰 생성 access: {}, refresh: {}", getClass().getName(), accessToken, refreshToken);


        //응답에 JWT 추가
        response.addHeader("Authorization", accessToken);
        response.addHeader("refresh_token", refreshToken);
        log.info("[{}} 응답 헤더에 토큰 담기", getClass().getName());

        //응답에 해당 회원의 추가정보 기입 여부 추가
        log.info("[{}} 응답에 해당 회원의 추가정보 기입 여부 추가", getClass().getName());
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");

        CheckActiveMemberDto checkActiveMemberDto;

        if (member.getStatus() == MemberStatus.PREACTIVE) {
            checkActiveMemberDto = new CheckActiveMemberDto(member.getNickname(), true);
        }else{
            checkActiveMemberDto = new CheckActiveMemberDto(member.getNickname(), false);
        }

        response.getWriter().write(objectMapper.writeValueAsString(checkActiveMemberDto));

        //redis에 refreshToken 저장하기((key, value): (token, username))
        redisUtil.setDataExpire(refreshToken, username, 8640_0000L);

    }



}

원래대로라면 onAuthenticationSuccess를 CustomSuccessHandler에서 재정의하였으므로 하위 메서드가 실행되어야 하지만 실행되지 않고 있었다.

 

 

 확인해보니 지금까지 상속받고 있었던 함수는 AuthenticationFilter.successfulAuthentication이었다. 해당 함수는 위 메서드와 달리 FilterChain을 받는다. 그러나 디버깅 시 실행되는 함수는 SimpleUrlAuthenticationSuccessHandler의 함수였다. 따라서 이에 맞게 고쳐주었다.

 @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
		
        중략

    }

이후 실행한 결과 응답이 정상적으로 오는 것을 확인할 수 있었다.