Conversation
- TokenService > TokenProvider - 비지니스 로직이 아니라 기능만 제공하는 것이라 Provider가 더 적절하다고 판단함
- 기존에 작성되지 않았던 것들도 작성함
- AS-IS: 액세스 토큰을 검증하면서 로그아웃했는지를 검증하고 있다. 이는 액세스 토큰의 검증부에 들어갈 것이 아니라, 더 이전 단계에서 처리되어야 한다. - TO-BE: 로그아웃 토큰을 필터에서 처리한다. 이전보다 더 빠르게 예외를 응답할 수 있다.
- 가독성 개선 - 다른 객체들에 책임 분산 - permitAllEndpoint에 대한 설정 제거
- 가독성 향상 - 필터 추가 - 시큐리티 단에서 관리하는 인증 필요없는 uri 제거
|
전체적으로 스프링 시큐리티의 구조와 필터 체인 활용이 명확해져서 훨씬 깔끔하고 유지보수성이 좋아진 것 같습니다. 몇 가지 생각해본 점과 제안 드리고 싶은 부분이 있어 공유드립니다! 😊 영서님께서 말씀하신 대로 SecurityConfig와 JwtAuthenticationFilter 양쪽에서 인증/인가를 중복 관리하는 것은 비효율적이라고 생각합니다. 다만, 모든 인증/인가 처리를 컨트롤러에서 담당하게 되면 컨트롤러의 책임이 과도해질 수 있을 것 같습니다. 그래서 저는 다음과 같은 방향은 어떨까 제안드립니다:
이렇게 하면 컨트롤러의 책임은 최소화하면서도, 보안 정책은 일관성 있게 유지할 수 있을 것 같은데 어떻게 생각하시는지 궁금합니다! 2️⃣ 스프링 시큐리티 필터를 활용 그런데 한 가지 궁금한 점이 있어서 질문드립니다!
이러면 SignOutCheckFilter는 RefreshToken의 상태만 확인하므로, 기존 AccessToken으로 다시 접근이 가능한 상황인지 궁금합니다. 제가 잘못이해했다면 알려주시면 감사하겠습니다 😂 3️⃣ 토큰 인증 필터에서 DB 접근하는 부분 제거 |
|
결론만 이야기하는 것보다 고민의 과정을 다 공유하는게 좋을 것 같아 좀 길게 적어봅니다.. ㅎㅎ; 1️⃣
그리고 SecurityConfig에서 중앙 관리하는 것에 대해서.. 정말 생각을 많이 해봤는데요😵💫 첫째로, uri 와 보안이 함께 묶이는 것이 좋지 않다 생각합니다. 그리고 SecurityConfig에서 통합으로 관리하는 방법에 대해 회의적이었던 또 다른 이유가 있는데요, 예를 들어 SecurityConfig에서 그런데 시간이 흘러 흘러.. 이 실수를 하지 않기 위해서는 SecurityConfig에서 보안은 횡단 관심사이기 때문에, SecurityConfig에서 중앙관리를 하면 이점이 있는 상황도 있다고 생각합니다. 하지만 지금 우리 api 들은 통일되지 않은 패턴을 갖는 경우가 더 많습니다. 그리고 횡단 관심사라면 차라리 컨트롤러의 ArgumentResolver 나, aop 로 어노테이션을 만들어서 사용하는게 적절치 않을까 생각합니다. 정리하자면,
는 생각입니다. |
|
2️⃣ |
|
추가 커밋으로
했습니다! 😊 |
저는 그동안 이렇게만 생각해왔어서 이게 적절하다고 생각하고 있었는데 이 사고 과정에서는 현재 저희 코드가 어떻게 구현되어있는가에 대한 생각이 빠져있던 거 같습니다 😅
이부분 읽으면서 다시 코드를 생각해보니 영서님이 말씀하신 대로 컨트롤러에서 구분하는 게 더 실수도 적을 거 같네요! 좋은 거 같습니다! |
| package com.example.solidconnection.auth.service; | ||
|
|
There was a problem hiding this comment.
TokenProvider는 Auth의 책임이니 적절하게 이동한 거 같습니다! 다만 앞으로 애플 등의 인증 방식이 추가될 수 있으므로, 확장성을 위해 인터페이스 도입을 고려하면 좋을 것 같습니다. 현재는 구조를 유지하면서, 새로운 인증 방식 추가 시점에 인터페이스화를 논의해도 좋을 거 같습니다!
| private static final String REISSUE_METHOD = "post"; | ||
|
|
||
| private final TokenProvider tokenProvider; | ||
| private final JwtProperties jwtProperties; |
There was a problem hiding this comment.
JWT 관련 설정값들을 한 곳(JwtProperties)에서 관리하니 더 깔끔해진 거 같습니다! 추후 재사용하기도 편하고 확장시킬 때도 편하겠네요!
| public class JwtUtils { | ||
|
|
There was a problem hiding this comment.
JWT 관련 유틸리티 코드가 한곳에 깔끔하게 모였네요 👍 그런데 이 유틸 함수는 static으로만 사용하니 실수로 인스턴스화를 하는 것을 방지하기 위해 private 생성자를 추가하는 건 어떨까요?
| } | ||
|
|
||
| @Test | ||
| void 유효하지_않은_토큰의_subject_를_추출하면_예외가_발생한다() { |
There was a problem hiding this comment.
저희 ~면_예외_응답을_반환한다로 통일할까요?
| import static org.junit.jupiter.api.Assertions.assertAll; | ||
|
|
||
| @DisplayName("JwtUtils 테스트") | ||
| class JwtUtilsTest { |
관련 이슈
특이 사항
위키에 정리해둔 스프링 시큐리티 관련 내용 읽고 리뷰해주세요!
https://github.com/solid-connection/solid-connect-server/wiki/Spring-Security-동작-원리
👇 전체적인 흐름은 아래와 같습니다

작업 내용
1/ uri 를 기준으로한 '인증 필수 여부' 분기 제거
SecurityConfiguration 과 JwtAuthenticationFilter 에 있었던, 인증 없이 통과되게 하는 uri 를 없앴습니다. c33466b
uri에 버전이 적용되면 uri를 관리하기 까다로워질 것입니다.
uri 관리 포인트를 최소화하기 위해서 uri 하드코딩을 지양할 필요가 있습니다.
그리고 'SecurityConfiguration에서 어떤 uri에 인증이 필요한지, 불필요한지 알고 있는게 맞나?🤨'라는 생각이 들더라고요.
uri 관련 내용은 컨트롤러에 두는게 더 응집도 있겠다고 생각했습니다.
2/ 스프링 시큐리티 필터를 활용
사실 이전 코드는 스프링 시큐리티에 대해서 이해를 많이 못하고 레퍼런스를 참고하며 코드를 작성했습니다😥
이번에는 제대로 공부했고! 스프링 시큐리티의 핵심인 '필터'를 적절히 활용해봤습니다.
우리는 쿠키가 아니라 토큰 + 헤더를 이용해 인증을 구현하고 있기 때문에, 로그아웃했으면 그 정보를 서버에 저장해야 합니다.
그렇지 않으면 로그아웃한 사용자가 같은 토큰으로 회원 기능을 사용하려 할 때, 마치 로그아웃 안한 것처럼 인증이 통과됩니다😱
이전에는 JwtAuthenticationFilter와 TokenValidator에 그 내용이 분산되어있었는데,
이를 SignOutCheckFilter로 옮겼습니다. b795eb8
3/ 토큰 인증 필터에서 DB 접근하는 부분 제거
기존에는 CustomUserDetailsService에서 '이 토큰의 subject에 해당하는 사용자가 정말로 있나?' 를 확인하기 위해서
ServiceRepository 로 DB에 접근했습니다.
하지만 이는 토큰의 장점을 살리지 못하는 구성이었습니다😔
물론 '이 토큰의 subject에 해당하는 사용자가 정말로 있나?'는 반드시 필요한 검증입니다.
하지만 이 검증이 들어가야 하는 부분이 jwt 검증 필터가 되어선 안된다고 생각합니다.
따라서 이 PR에서는 CustomUserDetailsService를 삭제했습니다.
사용자 검증은 서비스 단에서 이루어지게 해야 할 것 같은데 이 PR 변경 내용이 너무 많아져서.. 다음 PR에서 해보겠습니다!