Spring Security
Spring 기반 애플리케이션 보안(인증, 권한, 인가 등)을 담당하는 Spring 하위 프레임워크
인증과 권한에 대한 부분을 Filter흐름에 따라 처리
Filter는 Dispatcher Servlet 으로 가기 전에 적용 >> 가장 먼저 URL 요청받음
Interceptor는 Dispatcher와 Controller 사이에 위치, 적용 시기가 다름
Spring Security는 보안 관련해서 많은 옵션 제공 >> 개발자 입장에서 보안 관련 로직 하나하나 작성하지 않아도 됨
Spring Security 아키텍쳐
동작 방식
1. Request 수신
사용자가 Form을 통해 로그인 정보가 담긴 Request 전송
2. Token 생성
Authentication Filter가 Request 수신
UserNamePasswordAuthenticationToken (인증용 객체) 생성
UserNamePasswordAuthenticationToken은 해당 요청을 처리할 수 있는 Provider 탐색에 사용
3. AuthenticationFilter로부터 인증용 객체 수신
AuthenticationManager에게 처리 위임
AuthenticationManager는 List형태로 Provider 보유
4. Token을 처리할 수 있는 Authentication Provider 선택
실제 인증을 할 AuthenticationProvider에게 인증용 객체 전달
5. 인증
인증 절차가 시작되면 Authencitation Provider 인터페이스 실행
DB에 있는 사용자 정보와 입력받은 로그인 정보 비교
6. UserDetailsService의 loadUserByUsername 메소드 수행
AuthenticationProvider 인터페이스에서 authenticate() 메소드 오버라이딩
이 메소드의 파라미터로 인증용객체를 활용 >> 로그인 정보를 가져올 수 있음
7. DB 사용자 정보 가져오기 위해 UserDetailsService 인터페이스 사용
8. UserDetailsService 인터페이스는 입력된 로그인 username정보로 loadUserByUsername() 메소드 호출
DB에 있는 사용자 정보를 UserDetails 객체로 가져옴 (사용자 존재하지 않으면 예외 throw)
DB에서 가져온 사용자 정보와 로그인 정보 비교
일치 시 Authentication 참조 리턴, 일치하지 않으면 예외
9. 인증 완료
사용자 정보를 가진 Authentication 객체를 SecurityContextHolder에 저장
성공 시 AuthenticationSuccessHandler 실행
실패 시 AuthenticationFailurHandler 실행
인증(Authorization)과 인가(Authentication)
인증 (Authorization) : 해당 사용자가 본인이 맞는지 확인하는 절차
인가 (Authentication) : 인증된 사용자가 요청한 자원에 접근 가능한지 결정하는 절차
Spring Security는 기본적으로 인증 절차 거친 후 인가 절차 진행
인가 과정에서 해당 리소스에 대한 접근 권한이 있는지 확인
Spring Security에서는 인증, 인가를 위해 Principal을 아이디, Credential을 비밀번호로 사용하는 Crerdential 기반 인증방식 사용
Principal : 접근 주체, 보호받는 Resource에 접근하는 대상
Credential : 비밀번호, Resource에 접근하는 대상의 비밀번호
Spring Security 주요 모듈
SecurityContextHolder
보안 주체의 세부 정보를 포함
응용프로그램의 현재 보안 컨텍스트에 대한 세부 정보 저장
기본적으로
SecurityContextHolder.MODE_INHERITABLETHREADLOCAL 방법,
SecurityContextHolder.MODE_THREADLOCAL 방법 제공
SecurityContext
Authentication을 보관하는 역할
SecurityContext 통해 Authentication 객체 활용 가능
SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder.getContext().getAuthentication(authentication);
Authentication
현재 접근하는 주체의 정보와 권한을 담는 인터페이스
SecurityContext에 저장, SecurityContextHolder를 통해 SecurityContext에 접근
SecurityContext 통해 Authentication에 접근 가능
Public interface Authentication extends Principal, Serializable{
// 현재 사용자의 권한 목록 가져옴
Collection<? extends GrantedAuthority> getAuthroties();
// credentials (비밀번호)를 가져옴
Object getCredentials();
Object getDetails();
// Principal 객체 가져옴
Object getPrincipal();
// 인증 여부
boolean isAuthenticated();
// 인증 여부 설정
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
UsernamePasswordAuthenticationToken
Authentication을 Implements한 AbstractAuthenticationToken의 하위 클래스
User의 ID가 Principal 역할, Password가 Credential의 역할
첫 번째 생성자는 인증 전의 객체 생성, 두번째는 인증이 완료된 객체 생성
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer{
}
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
// 주로 사용자의 ID에 해당
private final Object principal;
// 주로 사용자의 Password에 해당
private Object credentials;
// 인증 완료 전의 객체 생성
public UsernamePasswordAuthenticationToken(Object principal, Ojbect credentials){
super(null);
this.principal = principal;
this.credentials = credentails;
setAuthenticated(false);
}
// 인증 완료 후 객체 생성
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extendes GrantedAuthority> authrorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // super 로 사용해야 됨
}
}
AuthenticationManager
인증에 대한 부분은 여기서 처리
실질적으로는 AuthenticationManager에 등록 된 AuthenticationProvider에 의해 처리
인증 성공하면 두번째 생성자를 이용해 객체 생성 >> SecurityContext에 저장
public interface AuthenticationManager {
Authentication authentication(Authentication authenticationi) throws AuthenticationException;
}
AuthenticationProvider
실제 인증에 대한 부분 처리
인증 전의 Authentication 객체를 받아 인증이 완료된 객체를 리턴하는 역할
아래와 같은 인터페이스를 구현하여 커스텀한 AuthenticationProvider 작성하고 AuthenticationManager에 등록하면 됨
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
boolean supports(Class<?> authentication);
}
Provider
AuthenticationManager를 Implements한 ProviderManager는 AuthenticationProvider를 구성하는 목록 보유
public class ProviderManager implements AuthenticationManager, MessageSoureceAware, InitializingBean {
public List<AuthenticationProvider> getProviders(){
return this.providers;
}
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문으로 모든 provider 순회하여 처리하고 result가 나올때까지 반복
for (AuthenticationProvider provider : getProvider()){ ... }
}
}
UserDetailsService
UserDetails 객체를 반환하는 하나의 메소드만 보유
일반적으로 이를 Implements한 클래스에 UserRepository를 주입받아 DB와 연결하여 처리
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
UserDetails
인증에 성공하여 생성된 UserDetails 객체는 Authentication 객체를 구현한 UsernamePasswordAuthenticationToken을 생성하기 위해 사용
UserDetails를 Implements하여 처리 가능
public interface userDetails extends Serializable {
// 권한 목록
Collection<? extends GrantedAuthroty> getAuthorities();
String getPassword();
String getUsername();
// 계정 만료 여부
boolean isAccountNonExpired();
// 계정 잠김 여부
boolean isAccountNonLocked();
// 비밀번호 만료 여부
boolean isCredentialNonExpired();
// 사용자 활성화 여부
boolean isEnabled();
}
GrantedAuthority
현재 사용자(Principal)가 가지고 있는 권한을 의미
ROLE_ADMIN, ROLE_USER와 같이 ROLE_?? 형태로 사용
GrantedAuthority 객체는 UserDetailsService에 의해 불러올 수 있고, 특정 자원에 대한 권한이 있는지 검사하여 접근 허용 여부 결정
'끄적 > BE' 카테고리의 다른 글
TCP UDP (0) | 2023.02.05 |
---|---|
Servlet (0) | 2023.01.18 |
JPA vs MyBatis (0) | 2023.01.11 |
JPA N+1 (0) | 2023.01.11 |
Spring / Spring Boot (0) | 2023.01.09 |