Programming/Spring Boot

Spring Security 시큐리티 동작 원리 이해하기 - 2

Jan92 2021. 9. 10. 00:22
 

Spring Security 시큐리티 동작 원리 이해하기 - 1

스프링 시큐리티 (Spring Security)는 스프링 기반 어플리케이션의 보안(인증과 권한, 인가)을 담당하는 스프링 하위 프레임워크입니다. 보안과 관련해서 체계적으로 많은 옵션들을 제공해주기 때문

wildeveloperetrain.tistory.com

첫 번째 포스팅에서 이어지는 내용입니다. 참고 부탁드리겠습니다.

 

AuthenticationProvider 인터페이스를 구현하는 클래스 만들기

 

@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final CustomUserDetailsService customUserDetailsService;
    private final PasswordEncoder passwordEncoder;
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
        
        UserDetails loadedUser = customUserDetailsService.loadUserByUsername(username);
        
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(loadedUser, null, loadedUser.getAuthorities());
        result.setDetails(authentication.getDetails());
        return result;
    }
    
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

실질적인 인증 로직을 위해 AuthenticationProvider interface를 구현한 클래스가 필요한데요.

AuthenticationProvider를 implements 하게 되면 authenticate(), supports() 메서드를 구현하게 됩니다.

 

* 위 구조는 보기 쉽도록 정말 필수적인 부분만 넣은 것이고 실제로는 예외처리 등의 로직이 더 들어가게 됩니다.

 

 

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
	private final Object principal;
	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
	}
}

UsernamePasswordAuthenticationToken Class 입니다.

첫 번째 생성자는 인증 전의 객체를 생성하고, 두 번째 생성자는 인증이 완료된 객체를 생성합니다.

 

 


 

 

세부적인 구현

 

@RequiredArgsConstructor
@Service
public class CustomUserDetailsService implements UserDetailsService {
    private final UserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("not found"));
    }
}

CustomUserDetailsService

 

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

UserRepository

 

 


 

 

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final CustomAuthenticationProvider authProvider;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authProvider);
    }
}

인증 로직이 구현된 CustomAuthenticationProvider를 ProviderManager가 알 수 있도록 ProviderManager에 등록해줘야 합니다.

시큐리티 설정을 위한 SecurityConfig 클래스에서 위와 같이 authenticationProvider를 추가해주면 됩니다.

 

 


 

SecurityContextHolder, SecurityContext

 

이렇게 구현된 인증 절차를 통해 인증이 완료되면 Authentication을 SecurityContextHolder 객체 안의 SecurityContext에 저장합니다. 

 

Authentication authentication = SecutiryContextHolder.getContext().getAuthentication();

그리고 이러한 방식으로 저장된 인증 객체를 전역적으로 사용할 수 있게 됩니다.

 

 

 

- SecutiryContextHolder

 

SecurityContext 객체를 저장하고 감싸고 있는 wrapper 클래스로 SecurityContextHolder는 보안 주체의 세부 정보를 포함하여 응용프로그램의 현재 보안 콘텍스트에 대한 세부 정보가 저장됩니다.

(MODE_THREADLOCAL, MODE_INHERITABLETHREADLOCAL, MODE_GLOBAL 세 가지 저장 방식이 있습니다.)

 

 

- SecutiryContext

 

Authentication을 보관하는 역할을 하며, SecurityContext를 통해 Authentication 객체를 꺼내올 수 있습니다. ThreadLocal에 저장되어 아무 곳에서나 참조가 가능하도록 설계되어 있습니다.

 

 

 

참고자료

https://jeong-pro.tistory.com/205
https://mangkyu.tistory.com/76s
https://springbootdev.com/2017/08/23/spring-security-authentication-architecture/