본문 바로가기

Proj/Tripot

로그인 인증 과정 추가해보기 - AuthenticationProvider 커스텀

1. 개요

해당 프로젝트에서는 관리자 계정으로만 Username password 방식으로 로그인이 가능해야 한다. 이전 포스팅에서의 동작을 기반으로 해당 인증과정을 추가해보고자 한다.

2. AuthenticationProvider

지난 포스팅에서 언급했듯이 인증 자체는 AuthenticationProvider에서 진행되고, 그 결과를 AuthenticationManager가 받는다. 인증 과정을 추가하려면 AuthenticationProvider를 수정하면 될 것이다.

    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            this.logger.debug("Failed to authenticate since no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String presentedPassword = authentication.getCredentials().toString();
            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                this.logger.debug("Failed to authenticate since password does not match stored value");
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }

DaoAuthenticationProvider의 패스워드 인증 로직이다. 이를 포함한 인증 과정을 그대로 포함하면서 관리자 인증 과정을 포함하고 싶었다.

public class CustomDaoAuthenticationProvider extends DaoAuthenticationProvider {

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        super.additionalAuthenticationChecks(userDetails, authentication);

        UserPrincipal userPrincipal = (UserPrincipal) userDetails;

        if (!userPrincipal.getMember().getRole().equals(MemberRole.ADMIN)) {
            this.logger.debug("Failed to authenticate since login cannot performed to not admin");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }
}

이에 따라 위 클래스를 상속하는 CustomDaoAuthenticationProvider를 만들어 해당 메서드를 오버라이드 하였다. UserPrincipal을 꺼내고, 해당 회원의 Role이 ADMIN이 아니면 예외를 던지고, 결과적으로 로그인을 실패시키도록 하였다.

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() throws Exception {
        DaoAuthenticationProvider daoAuthenticationProvider = new CustomDaoAuthenticationProvider();

        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());

        return daoAuthenticationProvider;
    }

커스텀한 Provider를 빈으로 새로 등록해주었다.

3. 테스트

유저 회원으로 로그인을 시도하면 다음과 같이 로그인에 실패하게 되고

관리자로 로그인할 때만 성공하는 것을 확인했다. 테스트를 위해 임의로 두 member의 비밀번호를 동일하게 설정하였다.

4. 여담

해당 구현은 시큐리티의 구조를 제대로 파악하지 못했다면 구별할 수 없었을 것 같다. 프레임워크를 제대로 사용하기 위해서 해당 프레임워크의 구조와 동작 방식을 왜 알아야 하는 지 몸소 깨닫는 경험이었다.