Spring/스프링 이론

[Spring Security] @AuthenticationPrincipal 로그인 정보 받아오기

블로그 주인장 2023. 12. 23.

@AuthenticationPrincipal 로그인 정보 받아오기

@AuthenticationPrincipal 을 이용해서 로그인 정보를 받아오는 방법에 대해 알아보겠습니다.


기존 방식


컨트롤러

세션에 저장되어 있는 member의 아이디를 이용해서 조회하여 사용

@GetMapping("/member")
public ResponseEntity<?> findMemberInfo(@RequestParam Long id) {
  return ResponseEntity.ok(memberService.findMemberInfo(id));
}

 

해당 방식으로 사용하면서의 불합리

1. 로그인 사용자의 정보가 필요할 때마다 매번 서버에 요청해서 DB에 접근해서 데이터를 가져온다.

2. 본인이 아닌데 타인의 정보를 확인할 수 있다. (ex. 본인은 1번 아이디인데, 1번 아이디의 token으로 3번 아이디 조회)

 

Authentication 객체 참조


Authentication 참조 방법

1. 컨트롤러에서 Principal 객체를 주입

  • Spring Security가 제공하는 SecurityContextHolder의 Principal 객체가 아닌 자바에 정의되어 있는 Principal 객체를 바인딩 해준다.
  • 사용할 수 있는 메소드가 getName() 밖에 없다.

2. 컨트롤러에서 @AuthenticationPrincipal 선언해서 엔티티 객체 받아오기

  • Entity에 있는 모든 필드를 참조 가능하다.

3. 컨트롤러에서 @AuthenticationPrincipal 선언해서 엔티티의 어댑터 객체 받아오기 (권장하는 방법)

  • 엔티티 객체를 필드로 갖고 있는 어댑터 클래스(DTO)를 생성하여 회원 객체(Member) 상속
  • 해당 어댑터 클래스의 엔티티 객체는 DB의 회원 객체의 정보를 담고 있어야한다.
  • UserDetailsService의 loadByUsername() 에서 어댑터 클래스를 반환하도록 수정한다.

 

@AuthenticationPrincial

세션 정보인 UserDetails에 접근할 수 있는 어노테이션이다.

 

@AuthenticationPrincipal

  • UserDetails 타입을 가지고 있기에 UserDetails 타입을 구현한 PrincipalDetails 클래스를 받아 User Object를 얻는다
  • userDetails(PrincipalDetails <T>).getUser()

@AuthenticationPrincipal UserAdapter 타입

  • 로그인 세션 정보를 어노테이션으로 간편하게 받을 수 있다.
  • UserDetailsService에서 Return한 객체의 파라미터를 직접 받아 사용 가능하다.
  • name 뿐만 아니라 다양한 정보를 받을 수 있다.
  • 중복 코드를 효율적으로 줄일 수 있다.

 

UserDetails 커스텀


UserDetails를 차후에 있을 Oauth 등을 함께 사용할 수 있게 UserDetails를 Custom 화를 진행

 

CustomUserDetails

public record CustomUserDetails(CustomUserDto memberDto) implements UserDetails {

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return List.of(new SimpleGrantedAuthority(memberDto.getRoleType()));
  }

  @Override
  public String getPassword() {
    return memberDto.getPassword();
  }

  @Override
  public String getUsername() {
    return memberDto.getEmail();
  }

  @Override
  public boolean isAccountNonExpired() {
    return true;
  }

  @Override
  public boolean isAccountNonLocked() {
    return true;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return true;
  }

  @Override
  public boolean isEnabled() {
    return true;
  }
}

 

CustomUserDetailsSevice

@Service
@RequiredArgsConstructor
public class CustomUserDetailService implements UserDetailsService {

  private final MemberRepository memberRepository;

  @Override
  public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    Member member = memberRepository.findByEmail(email)
        .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

    return new CustomUserDetails(CustomUserDto.fromEntity(member));
  }
}

 

TokenProvider

public Authentication getAuthentication(String token) {
  Claims claims = this.parseClaims(token);
  String userId = claims.get("userId").toString();

  CustomUserDetails customUserDetails =
      (CustomUserDetails) customUserDetailService.loadUserByUsername(userId);

  return new UsernamePasswordAuthenticationToken(customUserDetails,
      "", customUserDetails.getAuthorities());
}

 

 

변경 이후 방식


컨트롤러

@AuthenticationPrincipal 을 이용해서 User 정보를 조회한다.

UserDetails에서 Username으로 설정한 User ID or User Email 리턴한다.

 

  @GetMapping("/member/info")
  public ResponseEntity<?> findMemberInfo(@AuthenticationPrincipal CustomUserDetails userDetails) {
    return ResponseEntity.ok(memberService.findMemberInfo(userDetails.getUsername()));
  }

 

데이터 조회 방법 변경 이후

1. 한 번 인증된 사용자 정보를 세션에 담아놓고 세션이 유지되는 동안 사용자 객체를 바로 사용

2. 서버에 요청을 보낼 때의 인증 token의 정보를 가지고 본인 정보를 바로 확인

 

 

참고


https://velog.io/@shwncho/Spring-Security-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9D%B4%ED%9B%84-%EC%9C%A0%EC%A0%80-%EC%A0%95%EB%B3%B4-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0

 

https://velog.io/@jyleedev/AuthenticationPrincipal-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%A0%95%EB%B3%B4-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0

 

https://velog.io/@wlsgur1533/AuthenticationPrincipal%EC%97%90-null%EC%9D%B4-%EB%93%A4%EC%96%B4%EC%98%A8%EB%8B%A4

 

반응형

댓글