Skip to content
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

Open
wants to merge 21 commits into
base: haero77
Choose a base branch
from

Conversation

haero77
Copy link

@haero77 haero77 commented Feb 13, 2025

브라운 안녕하세요, 지난 1단계 미션 이후 2, 3, 4단계를 모두 구현하여 리뷰 요청드립니다!

이번에도 Spring Security 구현을 보면서 SecurityContextHolderFilter는 뭐하는 녀석일지, SecurityContextRepository에 SecurityContext를 언제 저장해야할지 찾아보며 공부해봤습니다.

구현을 뜯어보며 공부하느라 시간은 오래 걸렸지만,
FilterChainProxy -> SecurityFilterChain -> SecurityContextHolderFilter -> AuthenticationFilter -> AuthetncationManager -> AuthenticationProvider(인증된 Authentication 생성)이라는 인증의 큰 흐름이 드디어 머리 속에 잡혀서 기쁘네요 ㅎㅎ

지난 리뷰에서 남겨주신 더 생각해볼만한 부분들에 대해, 제 나름대로의 근거를 갖고 그 결론을 적용시켜봤습니다. (요건 코멘트로 남겨둘게요.) 요번에도 고민해볼만한 내용 많이 던져주시면 감사하겠습니다..!

첫 번째 미션 함께해주셔서 감사드려요 :-)

…FilterChain를 하나만 유지하는 구조로 변경

- applies feedback next-step#40 (comment)
… SecurityContextHolder에 적재하는 SecurityContextHolderFilter 구현
…ext를 저장하도록 변경

- SecurityContextPersistenceFilter는 securityContextRepository에 항상 context를 저장하지만,SecurityContextHolderFilter는 각 인증 필터에게 securityContextRepository를 저장할지 말지 자율권을 준다.
Comment on lines -15 to +17
public class BasicAuthenticationFilter implements Filter {
public class BasicAuthenticationFilter extends OncePerRequestFilter {
Copy link
Author

@haero77 haero77 Feb 13, 2025

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를 안 쓰는지 이건 좀 더 찾아봐야겠습니다.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OncePerRequestFilter의 동작 방법을 확인해보시면서 어떻게 요청당 한번 동작을 보장하는지 방법을 찾아보시면 좋을 것 같아요ㅋㅋ

Comment on lines 18 to 25
@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)
)
);
Copy link
Author

@haero77 haero77 Feb 13, 2025

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의 분리에 대한 기준점을 어떻게 잡아나가면 좋을지 조언해주시면 감사하겠습니다 :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀하신 대로 다른 인증 방식의 경우 다른 필터를 타야할 수 있는데 그럴 때 SecurityFilterChain을 분리하는게 좋을 것 같아요.
물론 matcher로 잘 분리할 수 있지만 같은 필터지만 다른 provider를 적용해야 하는 경우도 있을 수 있으니 그럴 땐 나누는게 좋겠네요.

Copy link
Contributor

@boorownie boorownie left a 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() {
Copy link
Contributor

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{
Copy link
Contributor

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에 대해 고민해보셨나요?

Comment on lines +8 to +14
/**
* 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
*/
Copy link
Contributor

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 주석은 어떤 의미인가요?

Comment on lines +49 to +51
HttpSession session = loginResponse.andReturn().getRequest().getSession();
assertThat(session).isNotNull();
assertThat(session.getAttribute("SPRING_SECURITY_CONTEXT")).isNotNull();
Copy link
Contributor

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 필터도 OncePerRequestFilter가 적절한지 앞서 남긴 코멘트에서 처럼 OncePerRequestFilter의 동작 방식을 같이 확인해보면 좋겠네요!

Comment on lines +25 to +28
public UsernamePasswordAuthenticationFilter(
UserDetailsService userDetailsService1,
SecurityContextRepository contextRepository
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecurityContextRepository를 생성자로 주입받을 때 어떤 장점이 있을까요?

Comment on lines +28 to +29
new UsernamePasswordAuthenticationFilter(userDetailsService, contextRepository),
new BasicAuthenticationFilter(userDetailsService, contextRepository)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

userDetailsService를 인자로 넘기는 것과 authenticationManager를 넘기는 방법도 있을 것 같은데
둘의 차이는 어떻게 생각하시나요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants