팀 미배정자 조회 API 개선 및 보안 강화

금은체리·2024년 6월 30일
0

1. 문제 정의 및 초기 상황

팀 미배정 사용자를 조회하는 API를 구현하는 과정에서 여러 가지 문제가 발생했습니다. 주요 문제는 다음과 같습니다:

  1. 팀 미배정 사용자 조회 시, 모든 트랙의 사용자가 조회됨:

    • GET 요청을 통해 특정 트랙에 속한 사용자를 조회해야 했지만, 모든 트랙의 사용자가 조회되는 문제가 발생했습니다.

    • 예상 원인: 쿼리나 서비스 로직에서 트랙 조건이 제대로 반영되지 않음

  2. 로그인 시 LazyInitializationException 발생:

    • 사용자 로그인 후 JWT를 생성하는 과정에서 Hibernate의 LazyInitializationException이 발생하여 세션을 초기화하지 못하는 문제가 있었습니다.
    • 예상 원인: 지연 로딩된 엔티티에 접근하려고 할 때 세션이 닫혀 있었기 때문
  3. API 접근 권한 문제:

    • ROLE_ADMIN 권한이 필요한 API 엔드포인트에 일반 사용자(ROLE_USER)도 접근할 수 있는 문제가 발생했습니다.
    • 예상 원인: Spring Security 설정이나 메서드의 권한 검증 로직에 문제가 있었을 가능성

2. 문제 해결 과정

2.1. 팀 미배정 사용자 조회 문제 해결

문제 분석:

  • 팀 미배정 사용자를 특정 트랙에 대해서만 조회해야 하는데, 모든 트랙의 사용자가 조회되는 문제가 발생했습니다.

해결 방안:

  • UserRepository에 JPQL 쿼리를 작성하여 trackId와 weekId를 조건으로 사용자 조회
  • UserService의 getUsersWithoutTeam 메서드를 수정하여 트랙과 주차 정보를 제대로 반영하도록 함
// UserRepository.java
@Query("SELECT u FROM User u WHERE u.userId IN " +
       "(SELECT tp.user.userId FROM TrackParticipants tp WHERE tp.track.trackId = :trackId) " +
       "AND u.userId NOT IN " +
       "(SELECT tm.user.userId FROM TeamMembers tm JOIN tm.team t WHERE t.trackWeek.trackWeekId = :weekId)")
List<User> findUsersWithoutTeam(@Param("trackId") Long trackId, @Param("weekId") Long weekId);
// UserService.java
@Transactional(readOnly = true)
public List<UnassignedUserResponseDTO> getUsersWithoutTeam(Long trackId, Long weekId) {
    Track track = trackRepository.findById(trackId)
            .orElseThrow(() -> new CustomException(ErrorCode.TRACK_NOT_FOUND));
    TrackWeek trackWeek = trackWeekRepository.findById(weekId)
            .orElseThrow(() -> new CustomException(ErrorCode.TRACK_WEEK_NOT_FOUND));

    List<User> users = userRepository.findUsersWithoutTeam(trackId, weekId);

    return users.stream()
            .map(user -> new UnassignedUserResponseDTO(user.getUserId(), user.getUsername(), user.getEmail()))
            .collect(Collectors.toList());
}

결과:

  • 수정 후, 특정 트랙에 속한 팀 미배정 사용자만 정확히 조회되는 것을 확인했습니다.

2.2. 로그인 시 LazyInitializationException 문제 해결

문제 분석:

  • Hibernate의 LazyInitializationException은 세션이 닫힌 상태에서 지연 로딩된 엔티티에 접근하려고 할 때 발생합니다.

해결 방안:

  • TrackParticipantsRepository에 fetch join을 사용하여 필요한 엔티티를 한 번에 로딩
  • UserService의 getUsersWithoutTeam 메서드에서 사용자를 조회할 때 지연 로딩을 피하기 위해 fetch join 사용
// TrackParticipantsRepository.java
@Query("SELECT tp FROM TrackParticipants tp JOIN FETCH tp.track t WHERE tp.user.userId = :userId")
List<TrackParticipants> findByUserUserId(Long userId);

결과:

  • fetch join을 사용하여 로그인 시 LazyInitializationException이 더 이상 발생하지 않음을 확인했습니다.

2.3. API 접근 권한 문제 해결

문제 분석:

  • ROLE_ADMIN 권한이 필요한 엔드포인트에 ROLE_USER도 접근할 수 있었습니다.

해결 방안:

  • Spring Security 설정에서 @EnableGlobalMethodSecurity 대신 @EnableMethodSecurity 사용
  • UserService의 메서드에서 SecurityContextHolder를 통해 현재 사용자 권한을 명시적으로 확인
// WebSecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
    // 기존 설정 유지
}

// UserService.java
@Transactional(readOnly = true)
public List<UnassignedUserResponseDTO> getUsersWithoutTeam(Long trackId, Long weekId) {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    if (principal instanceof UserDetailsImpl) {
        UserDetailsImpl userDetails = (UserDetailsImpl) principal;
        boolean isAdmin = userDetails.getAuthorities().stream()
                .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN"));
        if (!isAdmin) {
            throw new CustomException(ErrorCode.FORBIDDEN);
        }
    } else {
        throw new CustomException(ErrorCode.FORBIDDEN);
    }
    // 나머지 로직
}

결과:

  • ROLE_USER가 ROLE_ADMIN 권한이 필요한 엔드포인트에 접근할 수 없도록 제한했습니다.

3. 기술적 의사결정

  • JPQL 사용: 특정 트랙과 주차 조건을 적용하여 사용자 조회 로직을 구현하기 위해 JPQL 쿼리를 사용했습니다.
  • fetch join 사용: Hibernate의 지연 로딩 문제를 해결하기 위해 fetch join을 사용하여 필요한 엔티티를 한 번에 로딩하도록 했습니다.
  • Spring Security 설정 변경: 최신 Spring Security 버전에 맞게 @EnableMethodSecurity를 사용하고, 메서드 레벨에서 권한 검증을 명시적으로 수행하도록 수정했습니다.

4. 향후 계획

  • 추가적인 테스트를 통해 다른 부분에서도 동일한 문제가 발생하지 않는지 확인할 예정입니다.
  • 코드 리뷰를 통해 개선된 부분을 팀과 공유하고, 향후 유사한 문제 발생 시 신속히 대응할 수 있도록 문서화할 계획입니다.
  • 권한 관리 로직을 더욱 강화하여 프로젝트의 보안성을 지속적으로 향상시킬 예정입니다.
profile
전 체리 알러지가 있어요!

0개의 댓글