-
Notifications
You must be signed in to change notification settings - Fork 8
refactor: 스프링 시큐리티 코드 리팩터링 #154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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️⃣ |
|
추가 커밋으로
했습니다! 😊 |
저는 그동안 이렇게만 생각해왔어서 이게 적절하다고 생각하고 있었는데 이 사고 과정에서는 현재 저희 코드가 어떻게 구현되어있는가에 대한 생각이 빠져있던 거 같습니다 😅
이부분 읽으면서 다시 코드를 생각해보니 영서님이 말씀하신 대로 컨트롤러에서 구분하는 게 더 실수도 적을 거 같네요! 좋은 거 같습니다! |
Gyuhyeok99
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다!
| package com.example.solidconnection.auth.service; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TokenProvider는 Auth의 책임이니 적절하게 이동한 거 같습니다! 다만 앞으로 애플 등의 인증 방식이 추가될 수 있으므로, 확장성을 위해 인터페이스 도입을 고려하면 좋을 것 같습니다. 현재는 구조를 유지하면서, 새로운 인증 방식 추가 시점에 인터페이스화를 논의해도 좋을 거 같습니다!
| private static final String REISSUE_METHOD = "post"; | ||
|
|
||
| private final TokenProvider tokenProvider; | ||
| private final JwtProperties jwtProperties; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JWT 관련 설정값들을 한 곳(JwtProperties)에서 관리하니 더 깔끔해진 거 같습니다! 추후 재사용하기도 편하고 확장시킬 때도 편하겠네요!
| public class JwtUtils { | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JWT 관련 유틸리티 코드가 한곳에 깔끔하게 모였네요 👍 그런데 이 유틸 함수는 static으로만 사용하니 실수로 인스턴스화를 하는 것을 방지하기 위해 private 생성자를 추가하는 건 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
너~~무 좋아요! 제가 놓치고 있었네요😓
d9c965b
| } | ||
|
|
||
| @Test | ||
| void 유효하지_않은_토큰의_subject_를_추출하면_예외가_발생한다() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저희 ~면_예외_응답을_반환한다로 통일할까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
짚어주셔서 감사합니다🙇🏻♀️
605f24a
| import static org.junit.jupiter.api.Assertions.assertAll; | ||
|
|
||
| @DisplayName("JwtUtils 테스트") | ||
| class JwtUtilsTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트코드 가독성이 아주 좋네요 👍
관련 이슈
특이 사항
위키에 정리해둔 스프링 시큐리티 관련 내용 읽고 리뷰해주세요!
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에서 해보겠습니다!