뉴스피드 만들기 (팀프로젝트) v5: AutenticationPrincipal 인증로직

김재현·2023년 11월 26일
0

TIL

목록 보기
41/88
post-thumbnail

AutenticationPrincipal

뉴스피드를 조회했을 때, 로그인 여부에 따라 조회되는 글이 달라지게 만들고자했다. (전체 조회를 하되, 자신의 것은 안보이게)
이것을 어떻게 고쳐야할까 고민해봤다.

먼저는 '조건 없이 전체 조회'와 '자신의 것 제외 전체조회'의 요청 url을 다르게 만든 뒤, 로그인 이후부터는 '자신의 것 제외 전체조회'로 redirect 시키는 것이다.
이건 출력 결과는 동일하지만 매우 찝찝한 결과를 가져오는 코드였다.
뭔가 나중에 버그나기 딱좋은 코드랄까?

그래도 일단 작동은 잘 되니 넘어갔다.
그러다가 다른 공부를 하던 중... 혹시!? 하고

"MemberDetailImpl이 null 이라면" 이란 조건을 추가했더니 원하던데로 작동이 됐다!!

if (memberDetails == null) {
   List<Post> allPosts = postRepository.findAllByOrderByCreatedAtDesc();
   return convertToDtoList(allPosts);
   }

이게 흐름상 말은 되는 코드긴 한데 도대체 원리가 무엇인가 하고
다른 팀원 분께서 만들었던 JwtAuthorizationFilter의 코드를 들여다봤다.

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String token = jwtUtil.resolveToken(request);

        if(Objects.nonNull(token)  && jwtUtil.isTokenBlacklisted(token)){
            if(jwtUtil.validateToken(token)){
                Claims info = jwtUtil.getMemberInfoFromToken(token);

                String userId = info.getSubject();
                SecurityContext context = SecurityContextHolder.createEmptyContext();
                UserDetails memberDetails = memberDetailsService.getMemberDetails(userId);
                Authentication authentication = new UsernamePasswordAuthenticationToken(memberDetails
                        , null, memberDetails.getAuthorities());
                context.setAuthentication(authentication);
                SecurityContextHolder.setContext(context);

            } else{
                CommonResponseDto commonResponseDto = new CommonResponseDto("토큰이 유효하지 않습니다"
                        , HttpStatus.BAD_REQUEST.value());
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.setContentType("application/json; charset=UTF-8");
                response.getWriter().write(objectMapper.writeValueAsString(commonResponseDto));
            }
        }

        filterChain.doFilter(request, response);
    }

설정한 키값이 없으면 토큰값을 null, 있다면 정리해서 받은 뒤, 토큰이 유효한지 확인한다.
유효하다면 Principal에 유저 정보를 넣어주고, 아니라면 이러한 과정 없이 다음 필터로 이동한다.

그렇다면 토큰이 유효 할 때만 값이 담기기 때문에

'로그인 되지 않음' == 'Principal은 null' 로 볼 수 있는 것이다!

인증 로직을 더 자세히 들여다 보자면,

http.addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);

이건 이번 프로젝트에서 커스터마이징한 Config의 코드인데, UsernamePasswordAuthenticationFilter(UPAF) 앞에 커스터마이징 한 JwtAuthorizationFilter(JAoF)를 넣은 것을 알 수 있다.

로그인을 검증하는 경우,

  • UPAF는 사용자의 ID와 PW로 UsernamePasswordAuthenticationToken(UPAT)을 만들어 AuthenticationManager(AM)에게 넘겨 인증을 시도한다.
  • 실패하면 SecurityContextHolder(SCH)를 비우고,
  • 성공하면 SecurityContextHolder에 Authentication(UPAT)를 세팅(저장)한다.
    (UPAT: 인증된 사용자의 정보가 담기는 인증 객체인 Authentication의 종류 중 하나)

위의 커스터마이징한 코드와 비교해보자면,

  1. 우리가 만든 JAoF의 doFilterInternal에서 토큰을 받아와서 검증한 뒤,
  2. 토큰에서 Id를 받아와 Repository에 있는 유저 정보를 검색
  3. 유저 정보를 UPAT의 principal에 저장
  4. AM은 SCH를 확인. 만약 해당 토큰이 AM에서 처리 가능한 형태가 아니라면, 추가적인 인증 로직을 수행하거나 실패 처리
  5. AM은 이것을 UPAF에게 전달하여 실제 인증이 이루어진다.

JAoF가 로그인 정보를 전해주는 역할을 수행하는 것이다.

(JAoF는 JwtToken에 대해서만 인증했고 그 다음 UPAT에게 넘겨서 실제 인증을 거치는 것인데, 처음엔 Authentication이란 이름을 때문에 JAoF에서 모두 인증한다고 생각했다.)

단어가 너무 길어서 내맘대로 논문 처럼 줄여서 썼다. 갑자기 대학원의 기억이...

결론은 AutenticationPrincipal이 null이라면 어떠한 형태로든 인증이 안되었다는 것!


관련 포스팅

Previouse Post

Following Post

profile
I live in Seoul, Korea, Handsome

0개의 댓글