AuthenticationProvider
는 한 줄로 정의 하면 db에서 가져온 정보와 input 된 정보가 비교되서 체크되는 로직이 포함되어있는 인터페이스 라고 할 수 있다.
AuthenticationProvider
를 상속하면 authenticate
메소드를 오버라이드 할 수 있다.
authenticate
메소드의 경우 Authentication Manager
에서 넘겨준 인증객체(Authentication)를 가지고 메소드를 만든다.
Authentication
객체의 경우 사용자의 정보가 담겨져있다.
여기서 사용자는 form 또는 로그인 로직에서 가지고온 정보라고 할 수 있다.
Authentication
는 로그인 아이디, 로그인 비밀번호를 객체에서 가지고 올 수 있다.
db에서 사용자 정보를 가지고 오려면 UserDetailsService
에서 리턴해주는 값을 가지고 사용이 가능하다.
UserDetailsService
return 값의 경우 UserDetails
를 리턴해준다.
리턴된 user 정보를 가지고 PasswordEncoder
bean을 사용하여서 input 된 비밀번호와 매칭 시켜주면 된다.
여기서 PasswordEncoder
의 경우 우리가 configuartion
에서 bean으로 등록을 시켜준 클래스가 의존성 주입되어 있다.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
위의 코드의 경우 security 에서 비밀번호 인코딩에 대한 빈을 주입을 해주었다.
만약 해주지 않거나 커스텀 된 비밀번호 인코딩을 사용하고 싶다면
AuthenticationProvider
여기서 커스텀 해주면 된다.
@RequiredArgsConstructor
@Component("authenticationProvider")
public class DomainAuthenticationProvider implements AuthenticationProvider {
private final PasswordEncoder passwordEncoder;
private final DomainUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String loginId = authentication.getName();
String password = (String) authentication.getCredentials();
DomainUserDetails domainUserDetails = (DomainUserDetails) userDetailsService.loadUserByUsername(loginId);
if (isNotMatches(password, domainUserDetails.getPassword())) {
throw new BadCredentialsException(loginId);
}
return new UsernamePasswordAuthenticationToken(domainUserDetails, domainUserDetails.getPassword(), domainUserDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private boolean isNotMatches(String password, String encodePassword) {
return !passwordEncoder.matches(password, encodePassword);
}
}
supports
메소드의 경우 아래의 같이 공식 문서에 정의 되어있다.
AuthenticationProvider표시된 Authentication객체를 지원 하는지 여부를 반환 합니다.
return 된는 값이 true면 된다고 한다.
그렇다면 아래의 메소드는 무엇을 의미지..? 라고 느낄 수 있다.
대부분 여기를 그냥 ture 로 넘겨주거나 한다. 그래야 작동이 된다.
하지만 이 메소드의 경우 타입을 체크해주기 때문에 내가 사용하는 클래스 혹은 UsernamePasswordAuthenticationToken
인지 아닌지 유무를 체크를 해준다.
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
authenticate
메소드의 경우 위에서 짧게 설명을 하였지만 깊게 설명을 하자면
일단 인자의 경우 인증객체를 사용을 한다. 인증객체의 경우 인증 매니저 클래스에서 넘겨져온 인증 객체이다.
그렇다면 이 메소드의 경우 어떤걸 하나..?
이 메소드의 경우 DB에 인코딩된 비밀번호와 input 된 비밀번호를 비교를 해준다.
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String loginId = authentication.getName();
String password = (String) authentication.getCredentials();
DomainUserDetails domainUserDetails = (DomainUserDetails) userDetailsService.loadUserByUsername(loginId);
if (isNotMatches(password, domainUserDetails.getPassword())) {
throw new BadCredentialsException(loginId);
}
return new UsernamePasswordAuthenticationToken(domainUserDetails, domainUserDetails.getPassword(), domainUserDetails.getAuthorities());
}
위의 코드를 보면 인증 객체의 경우 input id & password 를 가지고 있다.
이것을 input id 로 userDetailsSerivce 에서 db 정보를 가지고 온다.
그리고 load 된 유저 정보를 가지고 우리가 설정된 비밀번호 빈을 가지고 매칭을 시켜준다.
만약 틀린 비밀번호시 BadCredentialsException
를 날려준다. 하지만 비밀번호가 정확하다면UsernamePasswordAuthenticationToken
로 리턴시켜준다.
그리고 support
메소드를 통해서 클래스 타입에 대한 체크를 해준 후 다음으로 넘어가 인증을 진행 혹은 끝을 낸다.
위의 코드의 경우 현재 @Component
에 서 bean 이름을 정의 해주었기 때문에 자동으로 빈주입이 된다.
하지만 또 다른 방식으로 설정 class 에서 주입을 줄 수 도 있다.
@Configuration
@EnableWebSecurity
@ComponentScan("com.baeldung.security")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DomainAuthenticationProvider authProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
configure
오버라이드 해서 커스텀한 클래스를 주입을 시켜주면 사용이 가능하다.
고맙습니다! 많은 도움이 되었습니다!