[jwt] 현재 로그인한 사용자의 정보를 얻어올 수 있는 방법 feat.로그인 안했으면요?..

RACOONMAN98·2022년 12월 24일
0

삽질일기

목록 보기
3/5

안녕하세요

구구절절한 내 사연

나도 내가 봤던것을 믿을 수 없지만 믿어달라

Api개발에 있어서 Member 도메인이 Post,comment,Likes등등 다양하게 엮여있다보니,
도메인 구조상 spring security + jwt의 개발이 선행 되어야 api가 완성되는 상황에서

어찌저찌 구글링과 스택오버플로우를 전전하며 디지털노마드 생활을 하루종일 하다보니
Postman에서 jwt도 성공적으로 응답헤더에 담기고 여러여러 로직들을 마친후에

생각해보니까~? 현재 로그인 한 사용자의 member 객체를 빼와야 하는 문제에 직면하게 되었는데.
막상 개발하기 전에는 '뭐 어떻게든 시작하면 되겠지' 하면서 개발하긴했지만,
일단 정신차려보니 완성은 되어있었고,,

디지털노마드 생활을 하는 도중에 까먹고싶지 않은 내용의 포스팅을 보게되어서 까먹지 않으려고

(사실은 나중에 써먹어보려고 )

포스팅을 시작해보려고 한다.

그래서 지금 로그인한 사람이 누구냐니까요.

나는 모르는데 SpringBoot는 알고있더라

처음에 알게 된 내용은 먼저 SecurityContext를 통해 로그인한 유저정보를 가져오는 방법이었다.
그 경위는 Spring Security 관련해서 공부하던중에

Authentication 객체가 사용자의 인증정보를 저장하고있고 인증 검증을위한 토큰으로써 쓰이게된다, 그렇다면 이친구가 저장되고 관리되는곳도 있어야하는데,
그곳이 바로 SecurityContext 고
SecurityContext안에 저장되어있는 인증객체들은 전역적으로 참조가 가능하다 !

(= 내가 빼올수 있겠는걸? 실제로 가능하다!)

Authentication authentication = SecurityContexHolder.getContext().getAuthentication();

이런식으로 SecurityContexHolder 에서 접근이 가능하다 그렇다면 정확히 무슨정보를 담고있냐~ 하면?

  • Authentication 객체 같은경우는
  • principal: User의 아이디나 User객체를 저장하고
  • credentials: 사용자의 자격증명서(=비밀번호)
  • authorities: 사용자의 권한목록(admin, user등등)
  • details: 인증 부가 정보
  • Authenticated: 인증여부

정보를 담고있다. 아무튼 이런 방법으로 객체를 얻어보려고 하니..

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = (User) authentication.getPrincipal();

이렇게 얻을 수 있었다 ! 메서드 추출해서 쓰면 될듯!?!?!...이라고 생각하던 그때

어노테이션으로 처리할수도있다는 글을 보고 더 조사해보았다 .
사실 방식은 위와 똑같지만 보기이쁜게 좋으니까..

Spring security 3.2 부터는 @AuthenticationPrincipal 을 이용해서 객체를 파라미터로 우겨넣을수가 있다.개쩌는데?

본인 같은 경우는 UserDetailsService를 구현한 memberDetailsService를 예제로 써보겠다 !

@Component
public class MemberDetailsService implements UserDetailsService {
	// ...의존성주입 관련 코드...
 @Override
    public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {

        Member findMember = getMember(userEmail);

        MemberDetails memberDetails = new MemberDetails(findMember);
        return memberDetails;
    }//memberDetails를 반환한다 !!!

    private Member getMember(String userEmail) {
        Optional<Member> optionalMember = memberRepository.findByUserEmail(userEmail);
        Member findMember = 
        optionalMember
        		.orElseThrow(() -> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));
        return findMember;
    } // db에서 member를 찾아오는 로직은 메소드 추출로 밖으로 빼놓았다 ! 왜냐면 ~~이게 이쁘니까 ..~~
}
private final class MemberDetails extends Member implements UserDetails {
        MemberDetails(Member findMember) {
            setId(findMember.getId());
            setUserName(findMember.getUserName());
            setUserEmail(findMember.getUserEmail());
            setUserPassword(findMember.getUserPassword());
            setUserImageUrl(findMember.getUserImageUrl());
            setRoles(findMember.getRoles());
        } //memberMedtails를 만드는 로직이다 !

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorityUtils.createAuthorities(this.getRoles());
        }

        @Override
        public String getPassword() {
            return getUserPassword();
        }

        @Override
        public String getUsername() {
            return getUserEmail();
        }

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

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

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

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

이렇게 MembersDetails 객체를 반환한다고 했을때,

놀랍게도 이렇게 적어주면 알아서 찾아서 Member 엔티티를 반환해준다;;
아무리봐도 스프링이 나를 놀리는것같아서 내가 속고 있는건가 ? 하고 조사를 해봤는데.

좋은 글을 찾을 수 있었다.

[Spring Security] @AuthenticationPrincipal 어노테이션은 어떻게 동작할까??

-> AuthenticationPrincipalArgumentResolver 라는 클래스를 활용하여 구현된 resolveArgument 메서드가 해당어노테이션이 붙은 바인딩된 파라미터를 찾아서 해당 객체의 Principal을 리턴한다..!
성능죽인다!

그래서 해당 방법으로 구현하려고했는데.. 만일 로그인을 안한 사용자가 요청을 보내온다면...인증객체가 없기때문에 null값을 해결해야하는데.. 라는 생각이 머리를 스쳐지나갔고
정신을 차려보니 한시간정도 멍때리다가 구글링을 시작했다..
피곤하면 자는게..?맞다 현재아침 7시부터 오후10시까지 코딩중인데 재밌다 지치지않는다 몸이 코딩이 게임같다

그래서 찾아낸것이 SpEL이라는 스프링 표현 방식인데 이게 아주 히트라고 생각해서 포스팅을 하게됐다. 참조한 블로그는 !
이 쪽입니다 !

SpEL은 간단하게 말하면 런타임시에 객체에 대한 쿼리와 조작을 지원하는 강력한(<-중요)표현 언어라고 한다 ! 진짜 강력해보인다
필자도 어노테이션을 커스텀해서 만들었는데 expression 부분 이후부터가 SpEL 이다.

풀어서 설명해보자면 익명인 경우에는 null로 설정을 하고 ~ 익명사용자가 아닌경우에는 실제 객체로 princpal을 꺼낸다 ! AccountAdapter 를 구현해서 연결해주기만 하면 된다 .!!

추가작성 ** ) null이라고 적었지만 Spring Security에선 null이아닌 anonymous인 익명사용자로 취급하고 익명사용자 토큰 객체를 만들어서( Anonymous Authentication Token <요거 구글링 하면 됩니다.) 관리한다고 한다.
자세한건 더 찾아보고 기회가 있으면 다음 포스팅때 공부해봐야겠다 !!.

이상입니다 !.

후기 ) 아주 유익한 하루였다 . 하루통째로 코딩하고 나니까 허리가 아프다;

profile
공부일기

0개의 댓글