- 프로젝트명: "뉴스피드 만들기"
- 프로젝트 소개: 내 게시물을 포함한 모든 게시물을 볼 수 있는 공간입니다.
- 시연 영상: https://www.youtube.com/watch?v=DeojTSjR9dY
- 사용 기술: #SpringBoot #JPA #MySQL #Redis
GitHub: https://github.com/k-jaehyun/NewsFeed
뉴스피드를 조회했을 때, 로그인 여부에 따라 조회되는 글이 달라지게 만들고자했다. (전체 조회를 하되, 자신의 것은 안보이게)
이것을 어떻게 고쳐야할까 고민해봤다.
먼저는 '조건 없이 전체 조회'와 '자신의 것 제외 전체조회'의 요청 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의 종류 중 하나)
위의 커스터마이징한 코드와 비교해보자면,
JAoF가 로그인 정보를 전해주는 역할을 수행하는 것이다.
(JAoF는 JwtToken에 대해서만 인증했고 그 다음 UPAT에게 넘겨서 실제 인증을 거치는 것인데, 처음엔 Authentication이란 이름을 때문에 JAoF에서 모두 인증한다고 생각했다.)
단어가 너무 길어서 내맘대로 논문 처럼 줄여서 썼다. 갑자기 대학원의 기억이...