TIL - #14 Spring Security와 인증

Quann·2022년 12월 27일
1

00. 개요

이번에 Spring Security, AOP, Exception Handler 등을 적용한 프로젝트에 저번에 작성했던 RefreshToken을 적용하기로 했다.
이로 인해, 이번에 배운 Security와 RefreshToken을 합치는 내 나름 대규모 작업을 진행했다. 이로 인해 Security, JWT 부분에 많은 변경사항이 생겼고, 이로 인해 코드가 꼬이기 시작했던 것 같다.

스프링 시큐리티를 조금 이해했다고 생각했는데, 막상 흐름에 관련된 문제를 마주치니 부족한게 많구나 싶었다.


01. 문제의 발견

다음과 같이 인증된 사용자가 리소스에 접근하는 메서드에 대해서, @AuthenticationPrinciapl을 통해 인증된 유저가 맞는지 먼저 체크하고자 했다.

하지만, 값이 받아와 지지 않아 디버그를 걸어보았더니, 모든 메서드에 대해 userDetails가 null 값으로 처리되고 있었다는 것을 확인했다.

확인했으니 고쳐야지.


02. 시도 방법

02.1. Filter

먼저, 가장 의심이 되는 필터쪽 부터 확인했다. ContextHolder에 정상적으로 Authentication 객체를 저장하지 못하고 있는 것 같아 확인해 봤지만, 사진에서 보이다 시피 토큰 값에서 username도 정확히 받아오고 있고, ContextHolder도 유효한 값을 컨텍스트에 넣어주고 있었다.

02.2. loadUserByUsername()

그렇다면, 사용자를 검증할때 항상 거치는 loadUserByUsername() 메서드가 문제가 아니었을까? 싶어 다시 확인해 보았다.

하지만 사진에서 보이다시피, username도 정상적으로 정상적으로 읽히고 정상적으로 값을 리턴해주고 있었다. 하긴, 정상적으로 리턴해주었으니 Filter에서 값을 받아올 수 있었겠지.


03. 문제의 해결

이 글을 읽으며, 무언가 이상한 점을 발견했는가?
그렇다면 내 5시간을 뺏어간 문제를 한 번에 해결할 능력을 가졌다는 것이다.
내 5시간 돌려줘요

접근에 대해 검증을 진행하는 @AuthenticationPrincipal UserDetailsImpl userDetail과..
접근의 유효성 검사시 불리는 loadUserByUsername()의 return 값 UserDetails..

UserDetailImpl userDetailUserDetails ...

UserDetailImplUserDetails ...

...

내 5시간 돌려줘요

즉, 객체 검증시 동작하는 loadUserByUsername()의 리턴값은 UserDetails이다.
이를 구체화한 UserDetailImpl로 인증 객체를 받으려고 쇼를 떨고 있었으니 당연히 안담긴다.

다음과 같이 인증 객체를 받을 타입을 맞춰 주었더니 null이 안뜨고 객체를 잘 담아주었다!

부모 객체는 자식 타입을 품을 수 있지만, 자식 객체는 부모 타입을 품을 수 없다!


04. 잡생각

이번에 RefreshToken도 구현해보고, Security도 구현해보며 요리조리 많이 배웠다고 생각했고, 흐름도 어느정도 이해했다고 생각했다.

그런데, 이번 오류를 만나면서 도대체 어디서부터 고쳐야 하는지, 어느 부분이 오류가 나는지 손을 대기 어려울 정도로 문제 파악에 애를 먹었다.

애를 먹었다는건 그만큼 내가 모른다는 뜻 ..

나는 아직 잘 이해를 못했고, 모르는 부분이 너무 많다.

시큐리티의 흐름이나 어떤 로직이 언제 작동하는지 더 자세히 알아보아야겠다.

내가 설정한 SecurityConfig를 기반으로 돌아가는 Security 에 얹어놓은 JWT와 거기에 구현해놓은 RefreshToken ...

열심히 해야지...


04. 오늘의 한 문단

내 5시간 돌려줘요


05. 22.12.28 추가내용 - UserDetails

이 코드를 작성한 이후, 계속해서 리팩터링을 진행하다가

UserDetails 객체로 컨트롤러 단에서 받아버리면, 내가 구현한 UserDetailsImpl 메서드나 내부 멤버들이 무용지물이 되어버린다. 그래서 내가 원하는 동작은 제한적으로 변하고 UserDetailsImpl 을 구현한 이유가 사라져버린다.

그래서, loadUserByUsername() 메서드의 리턴값을 UserDetailsImpl로 바꾸고 시큐리티 유저객체 검증 과정에서 UserDetailsImpl을 검증할 수 있도록 만들었다.
컨트롤러 단에서 내가 구현한 UserDetailsImpl에 대한 검증을 거칠 수 있게 되는거고 내가 코드를 더 확장할 수 있는 여지가 많아졌다.

이 게시글을 작성하며 고쳤던 오류는 고치는데 5시간이 걸렸지만, 이 다음 이슈를 만났을 때, 이 문제를 해결한 경험을 발판삼아 빠르게 코드 수정이 가능했다. 수정해야될 코드나 타야되는 로직은 더 많았는데도, 이 경험을 통해 깨닫고 나니 흐름이 더 잘보여 가능했던 것 같다.

5시간 돌려달라고 했지만, 값진 경험이니 돌려주지 않아도 됩니다.


06. 추가 한 문단

5시간 안돌려줘도 됩니다

profile
코드 중심보다는 느낀점과 생각, 흐름, 가치관을 중심으로 업로드합니다!

2개의 댓글

comment-user-thumbnail
2023년 1월 3일

어마무시하군요

1개의 답글