기존 RefreshToken 중복 오류 문제 (최초 로그인 후 로그아웃 하고 재로그인 시 실패하는 상황)

gminnimk·2024년 11월 9일
0

문제 해결

목록 보기
7/18



소셜 로그인 시 SQL 에러 (DB에 기존 리프레시 토큰이 존재하는 상황에서 로그인을 시도하면 중복 에러가 발생)




📜 문제 상황

  • 소셜 로그인(Kakao, Google)에서 'SQLIntegrityConstraintViolationException' 오류 발생.

  • 동일한 'user_id'가 새로운 'refreshToken'을 삽입하려고 시도하여 고유 제약 조건을 위반하는 'refresh_token' 테이블에 대한 중복 항목을 표현.

2024-11-09T16:28:11.042+09:00 WARN 26908 --- [ecogrow-backend][nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2024-11-09T16:28:11.042+09:00 ERROR 26908 --- [ecogrow-backend][nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Duplicate entry '3' for key 'refresh_token.UKf95ixxe7pa48ryn1awmh2evt7'
2024-11-09T16:28:11.062+09:00 ERROR 26908 --- [ecogrow-backend][nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.DataIntegrityViolationException: could not execute statement [Duplicate entry '3' for key 'refresh_token.UKf95ixxe7pa48ryn1awmh2evt7'][/* insert for com.sw.ecogrowbackend.domain.auth.entity.RefreshToken */insert into refresh_token (token_id,user_id,id) values (?,?,?)]; SQL [/ insert for com.sw.ecogrowbackend.domain.auth.entity.RefreshToken /insert into refresh_token (token_id,user_id,id) values (?,?,?)]; constraint [refresh_token.UKf95ixxe7pa48ryn1awmh2evt7]] with root cause
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '3' for key 'refresh_token.UKf95ixxe7pa48ryn1awmh2evt7'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:118) ~[mysql-connector-j-8.3.0.jar:8.3.0]




📜 문제 원인


📄 기존 소셜 로그인의 토큰 업데이트 로직 부족:

  • 카카오 소셜 로그인 메소드에서 동일한 user_id에 대한 기존 토큰을 확인하지 않고 새 refreshToken을 DB에 직접 저장.

  • 'RefreshTokenService.saveRefreshToken'을 사용하여 토큰 업데이트를 처리하는 일반 사용자 로그인과 달리 소셜 로그인은 이 서비스를 사용하지 않아 이전에 로그인한 사용자에게 중복 항목이 발생.

  • 통일성 없는 토큰 관리 논리:

    • 일반 로그인 로직은 'TokenResponseDto' 및 'RefreshTokenService.saveRefreshToken'을 사용하여 토큰을 관리하고 업데이트하여 중복 항목을 방지, 그러나 소셜 로그인 로직은 이러한 메소드를 사용하지 리프레시 토큰 중복 오류가 발생하고 고유 제약 조건 위반이 발생.



  • TokenResponseDto
package com.sw.ecogrowbackend.domain.auth.dto;

import lombok.Getter;

@Getter
public class TokenResponseDto {
    private Long userId;
    private String accessToken;
    private String refreshToken;

    public TokenResponseDto(Long userId, String accessToken, String refreshToken) {
        this.userId = userId;
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
    }
}




📜 문제 해결


📄 소셜 로그인 로직 수정:

  • refreshToken 관리를 위해 RefreshTokenService.saveRefreshToken을 사용하도록 KakaoService의 kakaoLogin 메서드를 업데이트.

    • 이 메소드는 user_id에 대해 refreshToken이 이미 존재하는지 확인하고 필요한 경우 이를 업데이트하여 중복 항목을 방지.
    /**
        * 토큰 저장
        *
        * @param userId       사용자 ID
        * @param refreshToken 리프레시 토큰
        */
       @Transactional
       public void saveRefreshToken(Long userId, String refreshToken) {
           User user = userRepository.findById(userId).orElseThrow(
               () -> new IllegalArgumentException("사용자를 찾을 수 없습니다.")
           );
           RefreshToken token = refreshTokenRepository.findByUserId(userId)
               .orElseGet(() -> new RefreshToken(refreshToken, user));
    
           token.updateToken(refreshToken);
           refreshTokenRepository.save(token);
       }
     
     

📄 카카오 로그인에서 TokenResponseDto를 사용:

  • 일반 로그인 프로세스와 일치하도록 accessToken 및 refreshToken을 모두 사용하여 TokenResponseDto를 반환하도록 카카오 로그인 방법을 수정.


📄 API 응답 통합:

  • 통합된 TokenResponseDto를 반환하도록 kakaoLogin API 엔드포인트를 수정하여 일반 로그인과 소셜 로그인 모두에서 일관된 API 응답 형식을 제공.


➡️ 보안 위험 및 일반 로그인 논리와 통일성을 위해 기존 소셜 로그인에 쿠키 변환 로직 삭제


📄 소셜 로그인 프론트 코드 수정 (로컬 스토리지에 저장하고 클라이언트에 나타나게 수정)





📢 코드 수정 전 후 결과 비교

📄 수정 전

  • Front

  • 클라이언트 확인



  • 클라이언트에 AccessToken이 인식되어 인증이 필요한 POST /api/waste/records , 즉 쓰레기 기록 생성 요청은 가능


  • 그러나 사용자별 쓰레기 기록을 조회하는 API, GET /api/waste/records/users/{userId}는 호출하지 못해 특정 유저의 쓰레기 기록을 기반으로 마이 페이지에 시각화를 실패하고 있음.

  • 수정 전 Service와 Controller 로직은 단순히 Accesstoken만을 반환하고 클라이언트에 응답하는 구조로 설정이 되어 있기에 refreshToken과 user_Id가 포함되어 있는 TokenResponseDto로 통합하여 클라이언트에서 요청 시 서버에서 클라이언트로 필요한 정보를 모두 응답하도록 설정.

    • 재로그인 시 RefreshToken 중복 문제

    • 사용자별 데이터를 가져올 수 있도록 {userId}를 조회 문제를 해결할 수 있음.







📄 수정 후

  • Front

  • 'const {accessToken, refreshToken, userId} = response.data;'

    • 구조 분해 할당을 사용하여 response.data에서 accessToken, refreshToken, userId 속성을 추출
    • accessToken: 서버에 대한 후속 요청을 인증하는 데 사용되는 토큰
    • refreshToken: 만료 시 accessToken을 새로 고치는 데 사용되는 토큰
    • userId: 로그인한 사용자의 고유 식별자



  • 클라이언트 확인

0개의 댓글