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. 여담
해당 구현은 시큐리티의 구조를 제대로 파악하지 못했다면 구별할 수 없었을 것 같다. 프레임워크를 제대로 사용하기 위해서 해당 프레임워크의 구조와 동작 방식을 왜 알아야 하는 지 몸소 깨닫는 경험이었다.
'Proj > Tripot' 카테고리의 다른 글
스프링부트 테스트코드 작성하기: 통합 테스트 (1) | 2025.01.10 |
---|---|
신고 기능 구현 (0) | 2025.01.04 |
application/json 형식으로 관리자 로그인 구현하기 (1) | 2024.11.29 |
스프링부트 테스트코드 작성하기: Controller (0) | 2024.11.22 |
스프링부트 테스트 코드 작성하기: Repository, Service (0) | 2024.11.18 |