스프링 기반 애플리케이션의 보안을 담당해주는 프레임워크이다. 서블릿 필터를 기반으로 이루어져있으며 보안과 관련된 많은 옵션을 제공해주기 때문에 개발자들은 보안 로직 보다는 주 로직에 관심을 쏟을 수 있게 도와준다.
Authentication(인증)
: 보호된 리소스에 접근하는 대상, 즉 사용자에게 적절한 접근 권한이 있는지 확인하는 절차Authorization(인가)
: 인증된 사용자가 요청한 자원에 접근 가능한지 결정하는 절차Principal(접근 주체)
: 보호된 리소스에 접근하는 대상Credential(비밀 번호)
: Principal의 비밀번호권한
: 인증된 주체가 어플리케이션의 동작을 수행할 수 있도록 허락되어 있는지를 결정Form Login 절차
사용자가 아이디(username) 와 비밀번호(password) 를 입력하고 인증 요청을 보냄
AuthenticationFilter
가 요청을 가로챈 후, 가로챈 정보를 통해 UsernamePasswordAuthenticationToken
이라는 인증용 객체를 생성
principal
에는 아이디를, credentials
에는 비밀번호를 넣어 둔다.UsernamePasswordAuthenticationToken(=Authentication
객체)타입은 해당 요청을 처리할 수 있는 Provider을 찾는데 사용(4번 참고)
AuthenticationFilter는 생성한 UsernamePasswordAuthenticationToken을 AuthenticationManager
에게 전달
AuthenticationManager는 List형태로 AuthenticationProvider
들을 갖고 있다.
구현체인 ProviderManager
가 갖고 있는 AuthenticationProvider들을 차례로 탐색하면서 각 Provider들의 supports(Class<?> authentication)
메소드를 호출하여 Authentication
의 타입 확인 후 UsernamePasswordAuthenticationToken를 전달
DaoAuthenticationProvider
가 작동한다고 들은 것 같다.@Override
public boolean supports(Class<?> authentication) {
// 이런 식으로 Provider가 Authentication의 타입을 검증하여 해당 타입이 맞다면 그 Provider로 실행
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
AuthenticationProvider는 실제 데이터베이스에서 사용자 인증정보를 가져오는 UserDetailsService
에 사용자 정보(아이디,username) 를 전달
UserDetailsService를 구현하여 loadUserByUsername(String username)
를 오버라이딩
loadUserByUsername메소드에서 넘겨받은 사용자 정보를 통해 DB에서 사용자 정보를 찾는 로직을 작성하고 찾은 사용자 정보를 담은 UserDetails
객체를 만들어 반환
UserDetails는 인터페이스기 때문에 직접 구현을 한 객체를 반환하던가,미리 구현해놓은 User
객체를 반환해도 된다.
인증용 객체(예를 들어 UserDetails를 구현한 객체)와 도메인 객체(실제 정보를 담은 Entity)를 분리하지 않기 위해서 실제 사용되는 도메인 객체에 UserDetails를 상속하기도 한다.
Authentication Provider가 데이터베이스에서 반환 받은 정보(UserDetails)와 Http Request를 통해 들어온 로그인 정보를 비교한다.
인증이 완료되면 권한 등의 사용자 정보를 담은 Authentication 객체를 반환한다.
스프링 시큐리티는 훨씬 다양한 필터체인을 사용하여 다양한 커스터마이징을 할 수 있는데, 모든 필터를 다 외우고 있을 필요는 없고 간략하게 뭐가 있는지만 확인해두고 필요할 때 제대로 찾아보면 될 것 같다.
SecurityContextPersistentFilter : SecurityContextRepository에서 SecurityContext를 가져와서 SecurityContextHolder에 주입하거나 반대로 저장하는 역할을 합니다.
LogoutFilter : logout 요청을 감시하며, 요청시 인증 주체(Principal)를 로그아웃 시킵니다.
UsernamePasswordAuthenticationFilter : login 요청을 감시하며, 인증 과정을 진행합니다.
DefaultLoginPageGenerationFilter : 사용자가 별도의 로그인 페이지를 구현하지 않은 경우, 스프링에서 기본적으로 설정한 로그인 페이지로 넘어가게 합니다.
BasicAuthenticationFilter : HTTP 요청의 (BASIC)인증 헤더를 처리하여 결과를 SecurityContextHolder에 저장합니다.
RememberMeAuthenticationFilter : SecurityContext에 인증(Authentication) 객체가 있는지 확인하고 RememberMeServices를 구현한 객체 요청이 있을 경우, RememberMe를 인증 토큰으로 컨텍스트에 주입합니다.
AnonymousAuthenticationFilter : 이 필터가 호출되는 시점까지 사용자 정보가 인증되지 않았다면 익명 사용자로 취급합니다.
SessionManagementFilter : 요청이 시작된 이후 인증된 사용자인지 확인하고, 인증된 사용자일 경우 SessionAuthenticationStrategy를 호출하여 세션 고정 보호 매커니즘을 활성화 하거나 여러 동시 로그인을 확인하는 것과 같은 세션 관련 활동을 수행합니다.
ExceptionTranslationFilter : 필터체인 내에서 발생되는 모든 예외를 처리합니다.
FilterSecurityInterceptor : AccessDecisionManager로 권한부여처리를 위임하고 HTTP 리소스의 보안 처리를 수행합니다.
Principal은 Authentication 객체(위 설명에선 UsernamePasswordAuthenticationToken
) 를 생성한다. 이 객체는 SecurityContext에 보관되어 사용된다.
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities(); // Authentication 저장소에 의해 인증된 사용자의 권한 목록
Object getCredentials(); // 비밀번호
Object getDetails(); // 사용자 상세정보
Object getPrincipal(); // ID
boolean isAuthenticated(); //인증 여부
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}