[SpringBoot] Spring-Security 정리

HandMK·2024년 6월 19일
0

SpringBoot

목록 보기
4/6
post-thumbnail
post-custom-banner

💻 버전 관리
Spring Boot : 3.2.0
springframework.security : 6.2.0
JDK : 17
Build : Maven

📌 서론

Spring-Security 를 처음 공부 했을 땐, 헷갈리는 부분도 많았고 '보안'이라는 생소한 영역을 접하는 것 같아 어려움이 있었지만, 차근차근 용어 정리 후에 구조를 보는 것이 도움이 많이 되어 이에 대해 정리하고자 한다.

공식문서에서 발췌한 인증 과정인데 이에 대한 용어 정리를 목표로 정리하고자 한다.

📌 본론

1. 인증 vs 인가

보안 용어에는 인증, 인가가 있다.

  1. 인증(Authentication) - 본인이 맞는지 확인하는 절차. ID/PW 를 입력
  2. 인가(Authorization) - '인증' 된 사용자가 어떤 자원에 접근할 수 있는지 확인 ex) 일반 사용자 vs 관리자

다음은 Spring-Security 의 Authentication Interface 이다.

Authentication Interface

public interface Authentication extends Principal, Serializable {
    
    // 현재 사용자의 권한 목록을 가져옴.
    Collection<? extends GrantedAuthority> getAuthorities();
    
    // credentials(주로 비밀번호)을 가져옴
    Object getCredentials();
    
    Object getDetails();
    
    // Principal 객체를 가져옴
    Object getPrincipal();
    
    // 인증 여부를 가져옴
    boolean isAuthenticated();
    
    // 인증 여부를 설정
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

💡 GrantedAuthority
ROLEADMIN, ROLE_USER 등 인증을 통과한 Principal 객체가 가지고 있는 권한을 나타내고, perfix(접두사)로 'ROLE' 이 붙는다.


2. SecurityContext / SecurityContextHolder

  • SpringContext : Authentication(인증정보)를 보관하는 역할
  • SpringContextHolder : SecurityContextHolder는 보안 주체의 세부 정보를 포함하여 응용 프로그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장된다.

3. UsernamePasswordAuthenticationToken

  • 위의 Authentication 을 구현한 AbstractAuthenticationToken의 하위 클래스
  • User ID 가 Principle / Password 가 Credential 역할을 한다.
    AbstractAuthenticationToken.java
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
}

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
    // 주로 사용자의 ID에 해당
    private final Object principal;
    // 주로 사용자의 PW에 해당
    private Object credentials;
    
    // 인증 전의 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false); // 여기 주목!!
	}
    
    // 인증이 완료된 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
			Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		this.credentials = credentials;
		super.setAuthenticated(true); // must use super, as we override
	}
}

4. AuthenticationManager


Authentication은 AuthenticationManager 에 등록된 AuthenticationProvider 에 의해 처리 된다.

인증이 정상 처리되면, boolean 값으로 위에서 봤던 AbstractAuthenticationToken 의 setAuthenticated(false) 을 true 로 변경 후 객체를 생성하여 SecurityContext에 저장한다.
AuthenticationManager Interface

public interface AuthenticationManager {
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

5. ProviderManager


실제 인증 과정에 대한 로직을 가지고 있는 AuthenticationProvider를 List로 가지고 있다.

ProviderManager에서 AuthenticationProvider를 for 문을 통해 조회 후 인증처리한다.

ProviderMangaer.java

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
    public List<AuthenticationProvider> getProviders() {
		return providers;
	}
    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		Authentication result = null;
		boolean debug = logger.isDebugEnabled();
        
        // for문으로 모든 provider를 순회하여 처리하고 result가 나올 때까지 반복
		for (AuthenticationProvider provider : getProviders()) {
            ....
			try {
				result = provider.authenticate(authentication);

				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException e) {
				prepareException(e, authentication);
				// SEC-546: Avoid polling additional providers if auth failure is due to
				// invalid account status
				throw e;
			}
            ....
		}
		throw lastException;
	}
}

6. AuthenticationProvider

  • 실제 Authentication 에 대한 부분을 처리하는 인터페이스
  • 인증 전 Authentication 객체를 받아서 인증 완료 된 객체를 반환하는 역할.
public interface AuthenticationProvider {

	// 인증 전의 Authenticaion 객체를 받아서 인증된 Authentication 객체를 반환
    Authentication authenticate(Authentication var1) throws AuthenticationException;

    boolean supports(Class<?> var1);
    
}

7. UserDetails

  • spring-security 에서 사용자의 정보를 담는 인터페이스
  • Authentication 객체를 구현한 UsernamePasswordAuthenticationToken 을 생성하기 위해 사용된다.
    @Override 해야 하는 메서드는 다음과 같음
Method-NameReturnDiscriptionDefault-Type
getAuthorities()Collection<? extands GrantedAuthority>계정의 권한 목록
getPassword()String계정의 비밀번호
getUserName()String계정의 고유한 값
isAccountNonExpired()boolean계정의 만료 여부true
isAccountNonLocked()boolean계정의 잠김 여부true
isCredentialsNonExpired()boolean비밀번호 만료 여부true
isEnable()boolean계정의 활성화 여부true

8. UserDetailsService


9. UserDetailsService

  • spring-security 에서 사용자의 정보를 가져오는 인터페이스
  • UserDetails 객체를 반환하는 유일한 메소드

정보를 가져와서 UserDetails 타입으로 반환

Method-NameReturnDiscriptionDefault-Type
loadUserByUsernameUserDetailsUserDetails

10. AuthenticationFilter

1. FilterChain

Spring-Security 는 Servlet-Filter 기반이다.

  • HttpRequest 요청이 FilterChain 에 들어간다. 여기서의 FilterChain 은 여러개의 필터가 연결된 것이다.

    💡 Servlet Container(톰캣) 은 URI 패스에 따라 어떤 FilterChain 과 Servlet 에 연결할 지 결정

FilterChain.java

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// do something before the rest of the application
    chain.doFilter(request, response); // invoke the rest of the application
    // do something after the rest of the application
}

💡 doFilter 메서드는 필터를 실행하기 전후로 코드를 넣을 수 있다. 때문에 순서에 유의해야 한다.

2. DelegatingFilterProxy

  • Spring이 제공하는 서블릿 Filter 구현체
  • Servlet Container 생명주기와 Spring ApplicationContext를 연결

    💡역할?
    Servlet Container에서 Filter 등록 해주지만, 우리가 사용하는 Spring에선 Bean으로 인식하지 않음.
    DelegatingFilterProxy 를 사용하면, 필터 클래스를 Bean으로 등록 할 수 있음.

3. FilterChainProxy

  • Spring-Security가 제공하는 FilterChainProxy 는 보통 스프링 빈이기 때문에, DelegatingFilterProxy 로 감싸져 있음.

    💡역할?
    요청에 따라 필요한 필터를 실행시키는 역할

4. SecurityFilterChain


SecurityFilterChainFilterChainProxy 가 어떤 필터 체인을 실행시켜야 하는지 파악하는데 사용한다.

SecurityFilterChain 안의 Security Filter 는 spring Bean 이다.

Security Filter 는 FilterChainProxy 에 등록되어있다.

  • FilterChainProxy가 spring-security에서 동작하는 모든 서블릿의 시작점이기 때문에 proxy에 있음.
  • FilterChainProxySecurityFilterChain 의 호출 시기 결정에 유연성을 가져다준다.
    • Filter 는 오직 URL에 따라 실행된다.
    • FilterChainProxy 는 요청의 RequestMarcher 인터페이스를 활용해 실행 시점을 파악한다.

4. Handling Security Exceptions

  • ExceptionTranslationFilter 는 AccessDeniedException 과 AuthenticationException 을 HTTP 응답으로 변환시켜준다.
  • FilterChain에 Filters 중 하나
  • FilterChain.doFilter(request, response) 실행해서 전체 애플리케이션 실행
  • 인증되지 않은 유저이거나 AuthenticationException 일 경우 인증을 시작
    • SecurityContextHolder 비움
    • RequestCache에 HttpServletRequest 저장
    • AuthenticationEntryPoint 는 유저에게 credentials(pw)를 요청할 때 쓴다.
  • 인증이 실패하면, AccessDeniedException이 발생! → AccessDeniedHandler 실행

📌 정리

✨ 인증과정 정리


1. 클라이언트로 부터 요청을 받으면 서블릿 필터에서 SecurityFilterChain으로 작업이 위임되고 그 중 UsernamePasswordAuthenticationFilter (AuthenticationFilter)에서 인증을 처리
2. AuthenticationFilter는 요청 객체(HttpServletRequest)에서 username과 password를 추출해서 토큰 생성
3. 이 후 AuthenticationManager에 토큰 전달, Manager는 인터페이스이며, 일반적으로 사용되는 구현체는 ProviderManager
4. ProviderManager는 인증을 위해 AuthenticationProvider로 토큰 전달
5. AuthenticationProvider는 토큰 정보를 UserDetailsService에 전달
6. UserDetailsService는 전달 받은 정보를 통해 DB에서 일치하는 사용자를 찾아 UserDetails 객체를 생성
7. 생성된 UserDetails 객체는 AuthenticationProvider로 전달되며, 해당 Provider에서 인증을 수행하고 성공하게 되면 ProviderManager로 권한을 담은 토큰 전달
8. ProviderManager는 검증된 토큰을 AuthenticationFilter로 전달
9. AuthenticationFilter는 검증된 토큰을 SecurityContextHolder에 있는 SecurityContext에 저장

Github

https://github.com/MinkeySon/springsecurity-study

profile
몫을 다하는 개발자
post-custom-banner

0개의 댓글