Sprgin Security Username대신 Email 인증 - f-lab-edu/jshop GitHub Wiki
개요
스프링 시큐리티의 UsernamePasswordAuthenticationFilter 를 사용해, 이메일과 패스워드로 인증하는 작업이다.
상세
시큐리티 필터중 UsernamePasswordAuthenticationFilter 는 시큐리티 필터체인 중간쯤에 위치한 필터이며 역할은 사용자의 이름 username 와 비밀번호 password 로 인증을 하는 필터다.
필터는 내부 메서드 attemptAuthentication 에서 유저 이름과, 비밀번호로 인증토큰을 생성하고 인증토큰의 검증을 AuthenticationManager 에게 위임한다. 성공하면 내부 메서드인 successfulAuthentication 이 수행되고, 실패되면 unsuccessfulAuthentication 이 수행된다.
이 과정에서 유저의 이메일과, 비밀번호를 인증토큰으로 전달하도록 구현했다.
AuthenticationManager 의 구현체는 ProviderManager 이며 이번 프로젝트에서는 건들지 않았다.
ProviderManager 에게 인증 토큰이 들어오면 내부적으로 DaoAuthenticationProvider 에게 전달된다.
DaoAuthenticationProvider 은 UserDetailsService 를 주입받으며 UserDetailsService.loadUserByUsername 메서드로 UserDetails 를 가져온다.
그리고 UserDetails.getUsername, UserDetails.getPassword 를 사용해 인증작업을 수행한다.
내가 구현한 부분은
UserDetailsService와UserDetails다.
UserDetailsService.loadUserByUsername는 인증토큰으로 넘어온 이메일을 사용해 유저를 찾아UserDetails타입으로 리턴해주는 역할을 한다.// CustomUserDetailService.java @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { User user = userRepository.findByEmail(email); ...유저 레포지토리에서 이메일을 사용해 유저를 찾고 이를
UserDetails로 래핑해 리턴한다.
인증 작업에서는 UserDetails.getUsername, UserDetails.getPassword 가 사용되므로, UserDetails 의 getUsername 메서드의 리턴값을, email 로 설정했다.
// CustomUserDetails.java @Override public String getUsername() { return user.getEmail(); }
후기
처음에는 JWT 강의를 따라하며 username과 password로 인증하는 방법에 대해 배우게 되었다.
하지만 유저 엔티티의 username 이 아닌 다른 컬럼(email) 로 인증을 하려하니 컴포넌트간 동작원리에 대해 이해가 부족해 진행할 수 없었다.
username 컬럼에 이메일을 저장하는 방법으로 간단하게 해결할 수 있었지만, 보다 근본적인 해결책을 원했다.
시큐리티 공식문서를 보며 필터와 컴포넌트간의 동작 원리에 대해 이해하니 원하는 동작을 수행할 수 있었다.
이 경험으로 다시한번 프레임워크와 라이브러리의 차이에 대해 확실하게 알게되었다.
프레임워크의 경우 개발자는 애플리케이션의 제어권이 없다.
스프링 시큐리티또한 정해진 순서대로 필터가 동작하며 개발자는 이 필터에 자신이 원하는 동작을 정의하는것 뿐, 애플리케이션의 제어를 하지 않는다.
이번 경우에도 인증을 위해
UserDetails타입의getUsername,getPassword가 사용되는건 알지만, 개발자가 메서드의 호출을 제어하지는 않는다. 또, 추상화에 대한 큰 장점또한 느꼈다. 위에서 말했듯 개발자는UserDetails.getUsername을 직접 호출하지 않는다.프레임워크 내부에서
getUsername을 호출하고, 이를 호출하기 위해 인터페이스인UserDetails에 의존한다.또한
UserDetailsService도 마찬가지로 인터페이스에 대한 의존만 하지 프레임워크로부터 구현체를 주입받아 동작하게 된다.프레임워크에 대한 이해와 추상화에 대한 이해가 한층 올라가는 경험이였다.