1. 개요
해당 프로젝트에서 소셜 로그인 파트를 맡고 개발을 하던 중이었다.
카카오 로그인 후 같은 페이지를 계속 리디렉션하는 현상이 발생하여 이를 해결하고자 하는 과정이다.
2. 해결
로깅 레벨을 debug로 바꾸고 이를 확인해보았다. 위 사진 이후 OAuth2UserService.loadUser가 실행되고, 다시 /로 리다이렉트되는 것을 확인할 수 있었다.
2-1. uri 권한 확인
위 사진에서 다음의 순서대로 동작하는 것을 확인할 수 있었다.
- /oauth2/authorization/kakao로 리다이렉트
- kauth.kakao.com/oauth/authorize으로 리다이렉트를 걸어서 인가 코드를 받아옴
- kauth.kakao.com/oauth/token으로 가서 토큰을 받아옴
- 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 {
중략
}
이후 실행한 결과 응답이 정상적으로 오는 것을 확인할 수 있었다.
'Proj > Tripot' 카테고리의 다른 글
스프링 시큐리티에서 발생하는 예외 처리하기 (0) | 2024.11.10 |
---|---|
Trouble Shooting: @Value에 값이 불러와지지 않는 문제 (3) | 2024.11.04 |
Spring OAuth2를 사용하여 소셜 로그인 구현하기 (feat.추가 정보 입력) (2) | 2024.11.01 |
프로젝트 DB 보안 간단히나마 관리하기 (1) | 2024.10.31 |
Trouble Shooting: 멀티 모듈 CI/CD, 환경 변수 설정 문제와 AWS Parameter Store을 이용한 해결 (1) | 2024.10.27 |