-
Notifications
You must be signed in to change notification settings - Fork 41
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
[인증(Authentication) - 2, 3, 4단계] 김선호 미션 제출합니다. 🚀 #42
base: haero77
Are you sure you want to change the base?
Conversation
…FilterChain를 하나만 유지하는 구조로 변경 - applies feedback next-step#40 (comment)
…ePasswordAuthenticationToken 구현
…icationProvider 구현
…onManager 구현체 ProviderManager 구현
…r를 상속하도록 변경 - applies feedback next-step#40 (comment)
…rityContext로 관리하도록 변경
… SecurityContextHolder에 적재하는 SecurityContextHolderFilter 구현
…ext를 저장하도록 변경 - SecurityContextPersistenceFilter는 securityContextRepository에 항상 context를 저장하지만,SecurityContextHolderFilter는 각 인증 필터에게 securityContextRepository를 저장할지 말지 자율권을 준다.
public class BasicAuthenticationFilter implements Filter { | ||
public class BasicAuthenticationFilter extends OncePerRequestFilter { |
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.
Filter 대신 OncePerRequestFilter 이나 GenericFilterBean을 고려해보면 어떨까요?
어떤 차이가 있을까요?
남겨주신 위 리뷰를 참고해서, 인증 필터가 OncePerRequestFilter
를 상속하도록 변경해봤습니다.
생각해보니 인증 필터가 HTTP 요청당 2번 이상 수행될 필요가 없을 것 같더라고요...!
(실제 Security 구현을 보니 GenericFilterBean을 상속하면서도 요청당 한 번 수행을 보장하고 있는 것 같던데, 왜 OncePerRequestFilter를 안 쓰는지 이건 좀 더 찾아봐야겠습니다.)
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.
OncePerRequestFilter의 동작 방법을 확인해보시면서 어떻게 요청당 한번 동작을 보장하는지 방법을 찾아보시면 좋을 것 같아요ㅋㅋ
@Bean | ||
public SecurityFilterChain formLoginSecurityFilterChain() { | ||
return new DefaultSecurityFilterChain( | ||
(httpServletRequest) -> httpServletRequest.getRequestURI().equals("/login"), | ||
List.of( | ||
new FormLoginFilter(userDetailsService) | ||
) | ||
); | ||
} | ||
|
||
@Bean | ||
public SecurityFilterChain basicAuthenticationSecurityFilterChain() { | ||
return new DefaultSecurityFilterChain( | ||
(httpServletRequest) -> httpServletRequest.getRequestURI().equals("/members"), | ||
List.of( | ||
new FormLoginFilter(userDetailsService), | ||
new BasicAuthenticationFilter(userDetailsService) | ||
) | ||
); |
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.
SecurityFilterChain을 각각 만들어 주셨네요!
만약 하나의 SecurityFilterChain으로 만들기 위해서는 어떻게 하면 좋을까요?
엔드포인트 1개 당 SecurityFilterChain 1개를 만드는 기존 구조에서, 엔드포인트 개수와 관계없이 SecurityFilterChain을 하나만 사용하도록 변경했습니다. 이제 엔드포인트가 추가될 때마다 SecurityFilterChain를 만들지 않아도 되니 유지보수 비용은 줄어드는데, 그럼 SecurityFilterChain은 어느 경우에 따로 만드는 것이 효율적인지에 대한 고민이 들더라고요.
찾아보고 고민해본 결과 인증 방식 자체가 달라지는 경우(JWT, Oauth 등)에 적용하면 효과적일 것 같은데, 막상 구현하다보면 그냥 인증 필터 내부에서 RequestMatcher로 잘 매칭시키면 되니까 또 SecurityFilterChain을 한 개로 유지해볼 수도 있겠다는 생각이 들더라고요. SecurityFilterChain의 분리에 대한 기준점을 어떻게 잡아나가면 좋을지 조언해주시면 감사하겠습니다 :)
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.
말씀하신 대로 다른 인증 방식의 경우 다른 필터를 타야할 수 있는데 그럴 때 SecurityFilterChain을 분리하는게 좋을 것 같아요.
물론 matcher로 잘 분리할 수 있지만 같은 필터지만 다른 provider를 적용해야 하는 경우도 있을 수 있으니 그럴 땐 나누는게 좋겠네요.
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.
인증 남은 미션 잘 수행해주셨습니다!
특별히 코드 개선의 코멘트를 남길 부분이 없어서
고민해보시면 좋을 부분에 대해 코멘트 남겼습니다!
확인해보시고 답변 남겨주세요~
@@ -17,20 +20,13 @@ public AppSecurityConfiguration(UserDetailsService userDetailsService) { | |||
|
|||
@Bean | |||
public SecurityFilterChain formLoginSecurityFilterChain() { |
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.
피드백 반영 👍
@@ -0,0 +1,12 @@ | |||
package nextstep.security; | |||
|
|||
public class AuthenticationException extends Exception{ |
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.
Exception을 상속한 이유가 있나요?
checked exception과 unchecked exception에 대해 고민해보셨나요?
/** | ||
* Attempts to authenticate the passed {@link Authentication} object, returning a | ||
* fully populated <code>Authentication</code> object if successful. | ||
* @param authentication the authentication request object | ||
* @return a fully authenticated object including credentials | ||
* @throws AuthenticationException if authentication fails | ||
*/ |
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.
주석도 가져오신건가요?ㅋㅋㅋ
throw new AuthenticationException("username or password invalid"); | ||
} | ||
|
||
// matches password. authenticated. |
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.
이 주석은 어떤 의미인가요?
HttpSession session = loginResponse.andReturn().getRequest().getSession(); | ||
assertThat(session).isNotNull(); | ||
assertThat(session.getAttribute("SPRING_SECURITY_CONTEXT")).isNotNull(); |
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.
이상한 스페이스 발견!
import java.util.Map; | ||
|
||
// 인증 정보를 추출하고, AuthenticationManager 에게 비밀번호 맞고 틀리는 것을 검증하는 책임을 위임한다. | ||
public class UsernamePasswordAuthenticationFilter extends OncePerRequestFilter { |
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.
이 필터도 OncePerRequestFilter가 적절한지 앞서 남긴 코멘트에서 처럼 OncePerRequestFilter의 동작 방식을 같이 확인해보면 좋겠네요!
public UsernamePasswordAuthenticationFilter( | ||
UserDetailsService userDetailsService1, | ||
SecurityContextRepository contextRepository | ||
) { |
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.
SecurityContextRepository를 생성자로 주입받을 때 어떤 장점이 있을까요?
new UsernamePasswordAuthenticationFilter(userDetailsService, contextRepository), | ||
new BasicAuthenticationFilter(userDetailsService, contextRepository) |
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.
userDetailsService를 인자로 넘기는 것과 authenticationManager를 넘기는 방법도 있을 것 같은데
둘의 차이는 어떻게 생각하시나요?
브라운 안녕하세요, 지난 1단계 미션 이후 2, 3, 4단계를 모두 구현하여 리뷰 요청드립니다!
이번에도 Spring Security 구현을 보면서 SecurityContextHolderFilter는 뭐하는 녀석일지, SecurityContextRepository에 SecurityContext를 언제 저장해야할지 찾아보며 공부해봤습니다.
구현을 뜯어보며 공부하느라 시간은 오래 걸렸지만,
FilterChainProxy -> SecurityFilterChain -> SecurityContextHolderFilter -> AuthenticationFilter -> AuthetncationManager -> AuthenticationProvider(인증된
Authentication
생성)이라는 인증의 큰 흐름이 드디어 머리 속에 잡혀서 기쁘네요 ㅎㅎ지난 리뷰에서 남겨주신 더 생각해볼만한 부분들에 대해, 제 나름대로의 근거를 갖고 그 결론을 적용시켜봤습니다. (요건 코멘트로 남겨둘게요.) 요번에도 고민해볼만한 내용 많이 던져주시면 감사하겠습니다..!
첫 번째 미션 함께해주셔서 감사드려요 :-)