본문 바로가기
Spring/Spring-Security

JWT 토큰 + @AuthenticationPrincipal

by YoonJong 2023. 2. 11.
728x90

@AuthenticationPrincipal 어노테이션을 사용하면 JWT 토큰 정보에 필터링 된 유저정보를 Controller 에서 간단히 가져올 수 있습니다.

 

이번 포스팅에서 변경해볼 사항은 SecurityContextHolder (이전) -> @AuthenticationPrincipal(변경) 해보겠습니다.

 

Spring Security 에서는 인증된 사용자 정보를 SecurityContextHolder 내부의 SecurityContext 에 Authentication 객체로 저장을 하고 이후 필요할 때 꺼내어 사용합니다.

 

SecurityContextHolder 에 저장된 회원을 꺼내어 사용하는 방법을 알아보겠습니다.

 

먼저 해당 어노테이션을 몰랐을 때, 사용했던 방법은 아래와 같은 방법을 통해 authentication 을 가져와 getName() 메서드를 사용하는 것이었습니다.

getName() 으로 String name 을 가져와서 memberRepository에서 회원을 찾는 방법입니다.

 

    private Member getMember() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return memberRepository.findByAccountId(authentication.getName()).orElseThrow(
                () -> new BusinessException(NOT_FOUND_MEMBER));
        
    }

위와 같은 로직으로 작성했을 경우, 많은 Service 클래스에 중복되는 코드가 발생한다는 점이 있습니다.

가장 큰 단점으로는 authentication 에서 사용할 수 있는 메서드가 getName() 만 있는 것입니다.

이유는 SecurityContextHolder 의 Principal 객체는 자바에 정의되어있는 Principal 객체를 바인딩하기 때문입니다.

 

@AuthenticationPrincipal 어노테이션을 사용해서 변경해보겠습니다.

 

예시는 게시글 작성API 입니다.

 

변경 전 Controller 

// 게시글 작성
@PostMapping("/boards")
@PreAuthorize("hasAnyRole('REALTOR','LESSOR','LESSEE')")
@ResponseStatus(HttpStatus.CREATED)
public void boardWrite(@RequestBody BoardRequestDto boardRequestDto) {
    boardService.save(boardRequestDto);
}

변경 후 Controller

// 게시글 작성
@PostMapping("/boards")
@PreAuthorize("hasAnyRole('REALTOR','LESSOR','LESSEE')")
@ResponseStatus(HttpStatus.CREATED)
public void boardWrite(@RequestBody BoardRequestDto boardRequestDto, @AuthenticationPrincipal User user) {
    boardService.save(boardRequestDto, user);
}

변경 전 Service

// 게시글 작성
@Override
public void save(BoardRequestDto boardRequestDto) {
    Member member = getMember();
    Board board = new Board(member, boardRequestDto);
    boardRepository.save(board);

}

    private Member getMember() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        authentication.getName();

        return memberRepository.findByAccountId(authentication.getName()).orElseThrow(
                () -> new BusinessException(NOT_FOUND_MEMBER));

    }

 

변경 후 Service

// 게시글 작성
@Override
public void save(BoardRequestDto boardRequestDto, User user) {

    Member member = memberRepository.findByAccountId(user.getUsername())
            .orElseThrow(() -> new BusinessException(NOT_FOUND_MEMBER));

    Board board = new Board(member, boardRequestDto);
    boardRepository.save(board);

}

변경 전 과 후의 차이는 코드량의 차이도 있지만, user 객체의 get 메서드를 통해 여러 정보를 가져와 활용할 수 있다는 점이라고 생각합니다.

 

+ @AuthenticationPrincipal(expression = "name") 을 사용할 수 있습니다.

name 에는 SpEL(스프링 표현언어) 를 사용합니다.

사용 방법은 아래 블로그에서 확인해 응용할 수 있습니다.

http://www.chidoo.me/index.php/2020/08/31/web-bearer-authorization-with-jwt-and-spring-security/

 

Spring-security를 활용한 JWToken 인증하기

퍼블릭 환경에서 제공되는 API 서비스를 만들 때 가장 고민되는 부분은 보안이다. API가 제공하는 기능이 민감한 정보가 아니라면 개발자 입장에서 행복하다. 하지만 값어치가 나가는 유료 정보

www.chidoo.me

 

728x90

댓글