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

1단계 - 인증(Authentication) 리뷰 요청 드립니다. #41

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

Conversation

yeongunheo
Copy link

안녕하세요 진영님

1단계 인증 step2~4에 대한 리뷰 요청드립니다.

FilterChainProxy가 여러 개의 SecurityFilterChain을 갖고 있는 형태로 변경하는 부분은 시간 상 구현하지 못했습니다.

적절한 패키지로 나누는 것도 한번 고민해보겠습니다.

감사합니다.

Copy link
Member

@jinyoungchoi95 jinyoungchoi95 left a comment

Choose a reason for hiding this comment

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

안녕하세요 영운님 😄

미션 잘 진행해주셨네요 👍 몇가지 코멘트 남겨두었으니 확인부탁드려요 😄

궁금하거나 고민이 되는 부분이 있으시다면 언제든 pr 코멘트 또는 dm으로 요청 부탁드립니다.
감사합니다 🙇‍♂️

private final String principal;
private final String credentials;
private boolean authenticated = false;
private UserDetails userDetails;
Copy link
Member

Choose a reason for hiding this comment

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

AuthenticationToken은 말그대로 인증된 토큰이기 때문에 UserDetails는 필요로 하지 않습니다.

UserDetails의 사용처를 생각해보면, 최초 인입된 유저가 인증이 가능한 유저인지 확인하기 위해서 사용될 뿐이지 이미 인증된 토큰에는 별도로 UserDetails를 사용하지 않습니다. 현재 만들어주신 코드에도 사용처가 없기도 하구요.

Comment on lines 10 to 13
public UsernamePasswordAuthenticationToken(String principal, String credentials) {
this.principal = principal;
this.credentials = credentials;
}
Copy link
Member

Choose a reason for hiding this comment

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

사용되지 않는 메소드들은 모두 제거해주시면 좋을 것 같아요 :)

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

Class<? extends Authentication> toTest = authentication.getClass();
Copy link
Member

Choose a reason for hiding this comment

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

toTest보다 적절한 네이밍은 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

target으로 변수명을 수정했습니다!

Comment on lines 23 to 44
for (AuthenticationProvider provider : this.providers) {
if (provider.supports(toTest)) {

try {
result = provider.authenticate(authentication);
if (result != null) {
break;
}
} catch (AuthenticationException ex) {
lastException = ex;
}
}
}

if (result != null) {
return result;
}
if (lastException == null) {
lastException = new ProviderNotFoundException(String.format("No AuthenticationProvider found for %s", toTest.getName()));
}

throw lastException;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
for (AuthenticationProvider provider : this.providers) {
if (provider.supports(toTest)) {
try {
result = provider.authenticate(authentication);
if (result != null) {
break;
}
} catch (AuthenticationException ex) {
lastException = ex;
}
}
}
if (result != null) {
return result;
}
if (lastException == null) {
lastException = new ProviderNotFoundException(String.format("No AuthenticationProvider found for %s", toTest.getName()));
}
throw lastException;
return providers.stream()
.filter(provider -> provider.supports(toTest))
.map(provider -> provider.authenticate(authentication))
.filter(Objects::nonNull)
.findAny()
.orElseThrow();

로 표현될 수 있을 것 같네요 :)

@@ -0,0 +1,40 @@
package nextstep.authentication.filter;
Copy link
Member

Choose a reason for hiding this comment

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

SecurityContextHolder를 관리하는 필터이기 때문에 context 패키지에 이동하는 것이 적절해보이네요~

filterChain.doFilter(servletRequest, servletResponse);
} finally {
securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
SecurityContextHolder.clearContext();
Copy link
Member

Choose a reason for hiding this comment

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

초기화 잘 넣어주셨네요 👍

Comment on lines 43 to 45
if (authenticationResult == null) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

인증이 null일 때 바로 return하면 필터 동작이 어디로 향할까요?

Copy link
Author

Choose a reason for hiding this comment

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

인증이 null일 때 다음 필터로 이동할 수 있도록 filterChain.doFilter(servletRequest, servletResponse); 코드를 추가했습니다!

filterChain.doFilter(servletRequest, servletResponse);
}

private Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
Copy link
Member

Choose a reason for hiding this comment

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

attempAuthentication하는 동작은 request에서 헤더를 보고 토큰을 읽어 authenticationManager.authenticate를 실행하는 과정인데요. 이 과정에서 SecurityContext는 사실 불필요한 동작입니다.

현재 필터에서의 동작은

  1. 헤더에 원하는 값을 뽑아내어 토큰을 생성한다.
  2. 토큰을 가지고 AuthenticationManager.authenticate를 실행한다.
  3. 성공하면 SecurityContext에 인증 값을 넣는다.
  4. 실패하면 초기화하고 401을 반환한다.

입니다. 즉, SecurityContext를 조작하는 행위는 모든 인증 과정이 끝나고 성공과 실패에 따른 행위이지 그 중간에 SecurityContext를 건들진 않아요.

Copy link
Author

Choose a reason for hiding this comment

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

SecurityContext를 사용하지 않으면 FormLoginTest.login_after_members() 테스트가 통과하지 않는데 테스트를 수정하는게 좋을까요?

Copy link
Author

Choose a reason for hiding this comment

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

SecurityContext를 사용하지 않으려고 아래처럼 수정을 시도했었습니다.

    private Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {

        String authorization = request.getHeader("Authorization");
        String credentials = authorization.split(" ")[1];
        String decodedString = Base64Convertor.decode(credentials);
        String[] usernameAndPassword = decodedString.split(":");

        String username = usernameAndPassword[0];
        String password = usernameAndPassword[1];

        UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);

        return authenticationManager.authenticate(authRequest);
    }

Comment on lines 14 to 20
HttpSession httpSession = request.getSession(false);
SecurityContext context = this.readSecurityContextFromSession(httpSession);
if (context == null) {
return SecurityContextHolder.createEmptyContext();
}

return context;
Copy link
Member

Choose a reason for hiding this comment

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

로직이 복잡할 필요 없이 loadContext는 단순히

  1. request.getSession의 값이 null이면 null을 반환한다.
  2. �httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY)를 반환한다.

면 됩니다. readSecurityContextFromSession의 분기는 위 과정으로 이미 동일하게 로직이 구성될 수 있어 별도로 필요 없어보여요.

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