back-end/spring boot

Spring Security 개념

study-minjeong 2024. 2. 29. 10:29

Spring Security에 대해 학습한 내용이다. 맨날 대충 코드 써먹기만 해서 제대로 이해해보려고 한다!

 

 

Spring Security는 Spring 기반의 애플리케이션의 보안을 담당하는 스프링 하위 프레임워크이다. 

인증과 인가와 같은 보안을 처리하는데 사용한다. 인증은 해당 사용자가 본인이 맞는지를 확인하는 절차이고, 인가는 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차이다.

 

Spring Security에서는 Principal을 아이디로, Credential을 비밀번호로 사용하는 Credential 기반의 인증 방식을 사용한다.

* Principal은 접근 주체로, 보호받는 Resource에 접근하는 대상이다. Credential은 비밀번호로 Resource에 접근하는 대상의 비밀번호이다. 

 

 

 

1. Filter chain

Request를 가로챈 후 일련의 절차를 처리한다. 다양한 filter 체인이 있는데, UsernamePasswordAuthenticationFilter는 사용자가 제출한 인증정보를 처리한다.

 

 

2. UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationFilter는 UsernamePasswordAuthenticationToken을 생성하여 AuthenticationManager에 전달한다. 토큰에는 사용자가 제출한 인증 정보가 포함되어 있다. User의 id가 Principal 역할을 하고, Password가 Crendential의 역할을 한다. 토큰의 첫번째 생성자는 인증 전의 객체를 생성하고, 두번째 생성자는 인증이 완료된 객체를 생성한다.

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); 
}

 

 

3. Authentication Manager

AuthenticationProvider를 통해 실제로 인증을 수행한다. 인증이 성공하면 2번째 생성자를 이용해 인증이 성공한 객체를 생성해 Security Context에 저장한다. 인증 상태 유지를 위해 세션에 보관하고, 실패한 경우에는 예외를 발생시킨다.

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

 

 

4. AuthenticationProvider

실제 인증에 대한 부분을 처리하고, 인증 전의 Authentication 객체를 받아서 인증이 완료된 객체를 반환하는 역할을 한다. 예시로 DaoAuthenticationProvider는 사용자 정보를 데이터베이스에서 가져와 인증을 수행한다. 또한, 직접 CustomAuthenticationProvider를 구현할 수 있다. CustomAuthenticationProvider를 등록하는 방법은 SecurityConfig에서 할 수 있다. 

 

 

5. PasswordEncoder

패스워드의 인코딩 방식을 지정한다. AuthenticationManagerBuilder.userDetailsService().passwordEncoder()를 통해 구현체를 지정할 수 있다.

 

 

6. UserDetailsService

AuthenticationProvicer는 이 서비스를 이용하여 사용자 정보를 가져온다. 사용자의 아이디를 받아 loadUserByUsername을 호출하여 해당 사용자의 UserDetails를 반환한다. 일반적으로 UserRepository를 주입받아 db와 연결하여 처리한다.

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

 

 

7. UserDetails

UserDetails 객체는 UsernamePasswordAuthenticationToken을 생성하기 위해 사용된다. 사용자의 아이디, 비밀번호, 권한 등이 포함되어 있다. 직접 개발한 UserVo 모델에 implements하여 처리하거나, UserDetailsVo에 implements하여 처리할 수 있다.

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();    
}

 

 

8. Authentication

인증 성공하면 AuthenticationProvider는 Authentication 객체를 생성하여 AutehnticationManager에 반환한다. 현재 접근하고 있는 사용자의 세부정보와 권한이 포함되어 있다. 이 객체는 SecurityContext에 저장되며, SecurityContextHolder -> SecurityContext를 통해 접근할 수 있다. 

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    Object getCredentials();
    Object getDetails();
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

 

 

9. SecurityContextHolder

현재 실행중인 스레드에 대한 SecurityContext를 제공한다.  

 

 

10. SecurityContext

현재 사용자의 Authentication을 보관하는 역할을 하며, SecurityContextHolder를 통해 사용자의 권한을 확인하고, 인가 결정을 한다.

 

 

11. GrantedAuthority

현재 사용자가 가지고 있는 권한이다. ROLE_*의 형태로 사용한다. 이 객체는 UserDetailsService로 불러올 수 있고, 특정 자원에 대한 권한이 있는지를 검사하여 접근 허용 여부를 결정한다.

 

 

 

 

 

참고

https://sjh9708.tistory.com/170

https://mangkyu.tistory.com/76