너어어어어는 인증도 네가하고 인가도 너가 다 만드니? 스프링은 모든게 준비되어 있단다.
스프링 시큐리티에서 중요하게 생각해야할 것은 인증
(너 누구니?), 인가
(뭘 허용하고 싶니?)다. 그리고 스프링 시큐리티는 이 둘의 확장 전략을 모두 갖춰놨다.
스프링부트 3.X, 스프링시큐리트 6.X 부터 설정이 변경되었다. 하지만 안의 동작이 달라진건 아니므로 이해하는데 있어 꼭 필요한 부분만 정리하겠다.
간단하게 보면 AuthenticationManager
가 Provider
를 호출하여 인증을 위임한다라고 할 수 있겠다.
가장 중요한 인터페이스는 AuthenticationManager
이고 다음과 같이 하나의 메소드를 가지고 있다.
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
구현체로 ProviderManager
가 있고 AuthenticationProvider
(인터페이스)로 인증을 위임하는 형태로 동작한다.
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
인증 방법이 여러 개가 있을 것이고 각각의 Provider 구현체가 있다.
다음은 Manager 구현체의 소스이다. support메소드
의 경우 Provider
클래스를 체크하여 지원하지 않는 Provider라면 건너띄게 된다.
다음의 소스는 ProviderManager
의 일부이다.
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
int currentPosition = 0;
int size = this.providers.size();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
...
url과 같이 리소스를 그룹지었을 때, url의 각 그룹들은 각각의 AuthenticationManager
를 가질 수 있다. 주로 ProviderManager
구현체를 부모로 갖는다.
(예를 들면 유저용, 어드민용 리소스가 그렇다. /user/**, /admin/**용으로 매니저를 각각 만들어 각 리소스에 맞게 인증을 진행할 수 있다.)
시큐리티 6버전에서 AccessDecisionManager와 Voter(AuthenticationManager와 Provider 관계)가 AuthorizationManager
로 통합되었다. 시큐리티 6버전에서 SecurityFilterChain bean을 만들 때 같이 설정할 수 있다. 다음은 AuthorizationManager 클래스이다.
@FunctionalInterface
public interface AuthorizationManager<T> {
default void verify(Supplier<Authentication> authentication, T object) {
AuthorizationDecision decision = check(authentication, object);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
}
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}
스프링시프링 시큐리티는 인증을 마치고 나면 인증 정보를 담는 SecurityContext
를 반환하여 스레드로컬에 저장한다. 그리고 다음에 오는 요청을 스프링시큐리티가 판단하여 이 요청과 스레드 로컬에 저장된 인증 정보를 연결하여 스프링 내에서 유저정보를 쉽게 꺼내볼 수 있게 해준다.