Day7. Spring Security : @AuthenticationPrincipal과 Principal 사용 시 Null 문제 해결하기

2ㅣ2ㅣ·2024년 10월 31일

Project

목록 보기
6/13

개요

특정 사용자의 데이터를 가져오기 위해서는 memberId 같은 PK값이 필요하다. 우리 프로젝트에서는 Spring Security를 사용했기 때문에, 컨트롤러단에 사용자 정보를 노출하지 않으면서 인증된 사용자 정보를 가져오기 위해 Principal 객체를 사용하려고 했다.

문제는 Principal이 null로 넘어오는 것이었다.
인텔리제이에서 디버깅 해봤을 때 SecurityContextHolder나 Principal이 제대로 출력되고, 할당되는 것도 확인했다.
근데 그게 컨트롤러단으로 넘어오자마자 null이 되는 것이다.
한마디로 인증 로직의 문제가 아니라 서블릿단에서 인증 정보를 읽지 못하고 있다는 것인데...

As-Is

결론부터 말하자면 @AuthenticationPrincipal 어노테이션과 Principal 객체를 같이 사용한 것이 문제였다.

Controller

@PostMapping("/write")
    public BaseResponse<DiaryResponseDto> writeDiary(
            @Validated @RequestBody DiaryRequestDto diaryRequestDto,
            @AuthenticationPrincipal Principal principal
            )
  • @AuthenticationPrincipal 어노테이션은 기본적으로 UserDetails 타입의 객체를 주입받는 것을 기대한다.
    • Spring Security가 인증된 사용자 정보를 관리하고 접근하는 표준 인터페이스UserDetails를 사용하기 때문이다.
  • 따라서, @AuthenticationPrincipal 어노테이션을 사용하려면, 컨트롤러의 파라미터 타입이 UserDetails 타입이거나, 이를 구현한 커스텀 타입이어야 한다.
    • Principal 대신 UserDetails의 구현체인 Member 객체를 주입했어도 된다는는 말이다.
  • 만약 Principal 타입으로 직접 주입받으려 할 경우, Spring Security는 타입 불일치로 인해 객체를 생성하거나 주입하지 못할 수 있다. 이로 인해, Principal 객체가 null로 넘어왔던 것이다.

To-Be

  • @AuthenticationPrincipal 어노테이션을 사용하는 대신, 컨트롤러 메서드에서 Principal 타입의 파라미터를 직접 선언하여 주입받았다.
  • 이렇게 하면 Spring Security는 SecurityContextHolder에서 Authentication 객체의 Principal 필드를 그대로 가져와서 Principal로 주입할 수 있다.

Controller

@PostMapping("/write")
    public BaseResponse<DiaryResponseDto> writeDiary(
            @Validated @RequestBody DiaryRequestDto diaryRequestDto,
            Principal principal
            ){

        if (diaryRequestDto == null){
            return new BaseResponse<>(BaseResponseStatus.BAD_REQUEST_INPUT);
        }
        System.out.println(principal + "하...");
        String email = principal.getName();

        return diaryService.writeDiary(diaryRequestDto, email);
    }

혹은 앞서 말했던 것처럼 UserDetails 구현체인 Member 객체를 직접 주입해도 괜찮다.

@PostMapping("/write")
    public BaseResponse<DiaryResponseDto> writeDiary(
            @Validated @RequestBody DiaryRequestDto diaryRequestDto,
            @AuthenticationPrincipal Member member
            )

컨트롤러단에서 문제 없이 사용자 인증 정보를 받아오는 것을 확인했다.
최대한 사용자 정보 노출을 줄이기 위해 Member 객체를 직접 사용하는 대신 @AuthenticationPrincipal 어노테이션을 제거하고 Principal 객체를 사용했다.

profile
https://sususoo.tistory.com/

0개의 댓글