AuthenticationProvider and Housekeeping
AuthenticationProvider and Housekeeping
AuthenticationProvider
고객의 요구사항은 다양할 수 있다. 예를 들어, 어떤 고객은 애플리케이션에서 인증을 수행하는데 유저이름과 비밀번호를 사용하고 싶을 수 있고, 어떤 고객은 OAuth2를 사용하고 싶을 수도 있다. 또 어떤 고객은 몇몇 서비스에 OTP를 적용하는 것을 고려할지도 모른다.
이렇게 수 많은 경우의 요구사항을 프레임워크에서 모두 준비해 줄 수 없기 때문에, 우리는 우리만의 AuthenticationProvider
를 커스텀하여 사용해야 한다.
AuthenticationProvider
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
boolean supports(Class<?> authentication);
}
AuthenticationProvider
내에는 두 가지 추상 메서드가 있다.
첫 번째는 Authenticate
메서드이다. 반환하는 Authentication
객체는 인증이 성공적이었는지에 대한 정보를 가지고 있어야 AuthenticationManager
가 다른 AuthenticationProvider
를 시도해 볼 수 있다.
두 번째는 supports
메서드이다. 해당 메서드는 우리가 커스텀하는 AuthenticationProvider
가 어떤 종류의 인증을 지원하고 싶은지를 서술한다. 어떤 종류의 인증에 관해 이 AuthenticationProvider
가 부름을 받아야 하는지 프레임워크에 알려주는 역할이다. 예시는 다음과 같다.
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
HouseKeeping
AuthenticationProvider
를 호출한 것은 필연적으로 AuthenticationManager
일 텐데, AuthenticationProvider
에서 반환한 Authentication
객체에 비밀번호와 같은 민감한 값이 들어있다면, 이것을 제거해줘야 한다.
AuthenticationManager
가 이 값을 제거하는 역할을 하며, 구현체 중 하나인 ProviderManager
는 다음과 같이 동작한다.
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//... 생략
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
provider.getClass().getSimpleName(), ++currentPosition, size));
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException ex) {
prepareException(ex, authentication);
throw ex;
}
catch (AuthenticationException ex) {
lastException = ex;
}
}
//...생략...
if (result != null) {
if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
((CredentialsContainer) result).eraseCredentials();
}
if (parentResult == null) {
this.eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
//...생략
}
- for문을 통해 각
AuthenticationProvider
가 현재Authentication
구현체를 지원하는지 확인한다. result
에 인증 정보가 반환된다.result != null
인 경우Authentication
객체의 credentials 정보를 파기한다.- 이로써
result
에는 인증완료(ture)여부만 남고, 비밀번호 값은 null이 된다.