Spring

Spring ) Spring Security 에 @AuthenticationPrincipal 사용시 예제

Albosa2lol 2023. 9. 5. 19:24

1. 개요

 

@AuthenticationPrincipal 어노테이션은 Spring Security를 사용할 때 현재 인증된 사용자의 정보를 가져오는 데 사용된다.

이를 활용하여 Controller 및 ServiceImpl 클래스에서 현재 인증된 사용자의 정보를 가져오는 부분을 변경하고자 함.

먼저, Controller에 해당 어노테이션을 추가하여 UserDetails를 가져온 후, ServiceImpl에 필요한 사용자 정보를 파라미터로 전달한다.

 

2-1. RecordController 변경 전

// class,class 위 어노테이션 생략

private final RecordService recordService;

@PostMapping
    public ResponseEntity<String> recordTime(@RequestBody RecordDto recordDto) {
        try {
            recordService.recordTime(recordDto.getRecordedTime());
            return ResponseEntity.ok("시간이 성공적으로 기록되었습니다.");
        } catch (RecordTimeException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("시간을 기록하는 중에 오류가 발생했습니다.");
        }
    }

2-2. RecordController 변경 후

// Class, Class 위 어노테이션 생략

@PostMapping
    public ResponseEntity<String> recordTime(@RequestBody RecordDto recordDto,
                                             @AuthenticationPrincipal UserDetails userDetails) {
        try {
            recordService.recordTime(recordDto.getRecordedTime(), userDetails);
            return ResponseEntity.ok("시간이 성공적으로 기록되었습니다.");
        } catch (RecordTimeException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("시간을 기록하는 중에 오류가 발생했습니다.");
        }
    }

recordTime 메서드의 파라미터로 '@AuthenticationPrincipal UserDetails userDetails' 가 들어간 것을 볼 수 있다.

 

 

2-3.RecordService 변경 전

public interface RecordService {
    void recordTime(String recordedTime);
}

2-4.RecordService 변경 후

public interface RecordService {
    void recordTime(String recordedTime, UserDetails userDetails);
}

recordTime 메서드에서 userDetails 를 RecordTimeController 를 통해 불러오기 위한 변경이다.

 

2-5.RecordServiceImpl 변경 전

private final AccumulatedTimeRepository accumulatedTimeRepository;
private final UserRepository userRepository;

@Override
    public void recordTime(String recordedTime) {
        try {

            // 현재 로그인된 사용자의 정보를 가져오는 부분 (이 부분은 Spring Security 을 통해 구현)
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication == null || !authentication.isAuthenticated()) {
                throw new RuntimeException("사용자가 로그인되지 않았습니다.");
            }
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            String username = userDetails.getUsername();  // 혹은 이메일, 당신의 시스템에서 사용하는 것에 따라 달라집니다.

            User currentUser = userRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다."));


            // 해당 사용자의 누적 시간을 조회하거나 새로 생성합니다.
            AccumulatedTime accumulatedTime = accumulatedTimeRepository.findByUser(currentUser)
                    .orElseGet(() -> {
                        AccumulatedTime newAccumulatedTime = new AccumulatedTime();
                        newAccumulatedTime.setUser(currentUser);
                        currentUser.setAccumulatedTime(newAccumulatedTime);
                        return newAccumulatedTime;
                    });

RecordServiceImpl 에서 자체적으로 Authentication 의 SecurityContextHolder 를 직접 호출하는 것을 볼 수 있다.

2-5.RecordServiceImpl 변경 후

@Override
    public void recordTime(String recordedTime, UserDetails userDetails) {
        try {
            String username = userDetails.getUsername();
            User currentUser = userRepository.findByUsername(username)
                    .orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다."));

            // 기존 코드 (변경 없음)
            AccumulatedTime accumulatedTime = accumulatedTimeRepository.findByUser(currentUser)
                    .orElseGet(() -> {
                        AccumulatedTime newAccumulatedTime = new AccumulatedTime();
                        newAccumulatedTime.setUser(currentUser);
                        currentUser.setAccumulatedTime(newAccumulatedTime);
                        return newAccumulatedTime;
                    });

            long newAccumulatedMinutes = accumulatedTime.getAccumulatedMinutes() +
                    parseRecordedTime(recordedTime);
            accumulatedTime.setAccumulatedMinutes(newAccumulatedMinutes);

            accumulatedTimeRepository.save(accumulatedTime);
        } catch (Exception e) {
            log.error("Error recording time", e);
            throw new RecordTimeException("시간을 기록하는 중에 오류가 발생했습니다.");
        }
    }

 @AuthenticationPrincipal 어노테이션을 활용하여 UserDetails 객체를 가져와 Service로 전달한 모습이다.

이로써 ServiceImpl에서는 SecurityContextHolder를 직접 호출하지 않고도 현재 인증된 사용자의 정보를 얻을 수 있다.

 

3. 요약

@AuthenticationPrincipal 을 쓰지 않으면 RecordServiceImpl 변경 전 처럼 SecurityContextHolder 에서 DB 조회를 할때마다 해야하는데, @AuthenticationPrincipal 를 쓰면 DB 조회 직접적으로 안해도 되어서 서버 리소스 사용하는게 줄어들게 된다.