[Spring Security] 스프링 시큐리티 인증/권한 부여 처리 흐름

ejoh·2023년 3월 16일
0

Spring Security

목록 보기
5/6

Spring Security의 인증 처리 흐름

인증 처리 흐름

  1. 사용자는 자신의 신원(Credentials)을 포함한 요청으로 로그인을 요청한다.

  2. Spring Security의 여러 Filter Chain 중 가장 먼저 UsernamePasswordAuthencticationFilter가 실행되고 아직 인증되지 않은 Authentication 정보를 생성한다.

  3. AuthenicationManager는 생성된 Authentication 정보를 토대로 인증 절차를 진행한다.

  4. AuthenticationProviderUserDetailsService를 통해 UserDetails를 조회한다.

  5. 조회한 UserDetails를 전달 받고 PasswordEncoder를 통해 UserDetails의 암호화된 Password와 인증을 위한 Authentication 안에 포함된 Password가 일치하는지 검증한다.

    5.1 검증에 실패하면 예외를 발생하고 인증 처리가 중단된다.

  6. 검증에 성공하면 인증이 된 UserDetailsAuthentication으로 변환하여 생성한다.

  7. ProviderManagerUsernamePasswordAuthenticationFilter를 거쳐 SecurityContext에 인증된 인증을 저장한다.

  8. 최종적으로 SecurityContext에는 Principal과 Credentials, 권한과 관련된 부분들이 저장된다.

UsernamePasswordAuthenticationFilter

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
	//...
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		if (this.postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
		String username = obtainUsername(request);
		username = (username != null) ? username.trim() : "";
		String password = obtainPassword(request);
		password = (password != null) ? password : "";
		UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
				password);
		setDetails(request, authRequest);
		return this.getAuthenticationManager().authenticate(authRequest);
	}
    //...
}

클라이언트로부터 전달 받은 Credential을 Spring Security가 인증 프로세스에서 이용할 수 있도록 UsernamePasswordAuthenticationTokenunauthenticated(username, password) 메서드로 토큰을 생성하는 역할을 한다.

클래스 이름은 Filter이지만, 실질적인 Filter의 역할은 AbstractAuthenticationProcessingFilter에서 진행된다.

if (this.postOnly && !request.getMethod().equals("POST"))

해당 조건문 때문에 HTTP Method가 POST인 경우에만 처리되며, 그렇지 않으면 예외를 던진다.

AbstractAuthenticationProcessingFilter

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
		implements ApplicationEventPublisherAware, MessageSourceAware {
}

HTTP 기반의 인증 요청을 처리하지만 실질적인 인증 시도는 하위 클래스에게 맡기고, 인증에 성공하면 인증된 사용자의 정보를 SecurityContext에 저장하는 역할을 한다.

doFilter() 메서드를 가지고 있어 사용자의 로그인 요청을 가장 먼저 전달받는다.

Authentication

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

스프링 시큐리티에서 인증을 표현하는 인터페이스이다.

  • Principal

    • 사용자를 식별하는 고유 정보

    • 일반적으로는 Username이, 다른 인증 방식에서는 UserDetails가 고유정보가 된다.

  • Credentials

    • 사용자 인증에 필요한 정보

    • 인증 이후, ProviderManager에 의해 삭제된다.

  • Authorities

    • AuthenticationProvider에 의해 부여된 사용자의 접근 권한 목록

    • 일반적으로 GrantedAuthority 인터페이스의 구현 클래스로 SimpleGrantedAuthority를 사용한다.

AuthenticationManager

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

인증 처리를 총괄하는 매니저 역할을 하는 인터페이스이다.

Filter는 AuthenticationManager를 통해 느슨한 결합을 유지하며, 인증을 위한 실질적인 관리는 구현 클래스를 통해 이뤄진다.

ProviderManager

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
	//...
	private List<AuthenticationProvider> providers = Collections.emptyList();
	private AuthenticationManager parent;
    //...
}

AuthenticationManager를 구현하는 클래스이다.

스프링 시큐리티가 생성하고 등록하는 스프링 빈이므로 직접 구현할 필요는 없다.

AuthenticationProvider를 관리하고, AuthenticationProvider에게 인증 처리를 위임하는 역할을 한다.

인증에 성공하는 경우 AuthenticationManager 인터페이스의 메서드인 authenticate() 메서드의 반환 값으로 Authentication 객체 안에 인증 값을 넣게 된다.

AuthenticationProvider

public interface AuthenticationProvider {
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
    boolean supports(Class<?> authentication);
}

AuthenticationManager로부터 인증 처리를 위임받아 실질적인 인증 수행을 담당하는 컴포넌트이다.

UserDetails

public interface UserDetails extends Serializable {

	Collection<? extends GrantedAuthority> getAuthorities();
	String getPassword(); 
	String getUsername(); 

	boolean isAccountNonExpired(); 
	boolean isAccountNonLocked();  
	boolean isCredentialsNonExpired(); 
	boolean isEnabled();               
}

데이터베이스 등의 저장소에 저장된 사용자의 Username과 사용자의 자격을 증명하는 Credential, 사용자의 권한 정보를 포함하는 컴포넌트이다.

UserDetailsService

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

loadUserByUsername()으로 UserDetails를 로드하는 핵심 인터페이스이다.

SecurityContext, SecurityContextHolder

public class SecurityContextHolder {
	//...
    // SecurityContext에 연결하는 객체 
	private static SecurityContextHolderStrategy strategy; 
    //...
}

SecurityContext

Authentication 객체를 저장하는 컴포넌트이다.

SecurityContextHolder

SecurityContext를 관리하는 역할을 담당

Spring Security는 SecurityContextHolder에 의해 SecurityContext의 값이 채워져 있다면 인증된 사용자로 간주한다.

Spring Security의 권한 부여 처리 흐름

권한 부여 처리 흐름

인증된 사용자임을 확인하는 경우, 인가된 사용자인지 사용자의 권한을 확인하는 처리가 필요하다.

  1. Filter를 통해 인증 처리가 성공한 경우 AuthorizationFilter를 실행한다.

  2. 인증된 사용자 정보를 통해 권한을 얻는다.

  3. AuthorizationFilter에 있는 AuthorizationManager에서 권한에 대한 처리를 담당한다.

AuthorizationFilter

URL을 통해 사용자의 액세스를 제한하는 권한 부여 Filter이며, Spring Security 5.5 버전부터 FilterSecurityInterceptor를 대체

AuthorizationManager

권한 부여 처리를 총괄하는 매니저 역할을 하는 인터페이스

RequestMatcherDelegatingAuthorizationManager

AuthorizationManager의 구현 클래스 중 하나이며, 직접 권한 부여 처리를 수행하지 않고 RequestMatcher를 통해 매치되는 AuthorizationManager 구현 클래스에게 권한 부여 처리를 위임

RequestMatcherSecurityConfiguration에서 .antMatchers("/orders/**").hasRole("ADMIN") 와 같은 메서드 체인 정보를 기반으로 생성

접근 제어 표현식

httpSecurity 객체에서

httpSecurity
			.authorizeHttpRequests(authorize ->  authorize
            .antMatchers("/**").permitAll());

antMatchers() 처럼 authorize. 으로 사용할 수 있다.

표현식설명
hasRole(Stirng role)- 현재 보안 주체(principal)가 지정된 역할을 가지는지
확인하고 가지고 있으면 true를 반환

- hasRole(’admin’)처럼 파라미터로 넘긴 role
ROLE_ 로 시작하지 않으면 기본적으로 추가

- DefaultWebSecurityExpressionHandler의
defaultRolePrefix를 수정하면 커스텀 가능
hasAnyRole(String… roles)- 현재 보안 주체가 지정한 역할을
하나라도 갖고 있다면 true 반환

- 문자열 리스트를 콤마로 구분해서 전달한다.

ex) hasAnyRole(’admin’, ‘user’)
hasAuthority(String authority)- 현재 보안 주체가 지정한 권한을 가지는지
확인하고 가지고 있으면 true 반환

ex) hasAuthority(’read’)
hasAnyAuthority(String… authorities)- 현재 보안 주체가 지정한 권한 중
하나라도 있으면 true 반환

ex) hasAnyAuthority(’read’, ‘write’)
principal- 현재 사용자를 나타내는 principal
객체에 직접 접근 가능
authentication- SecurityContext로 조회할 수 있는 현재
Authentication 객체에 직접 접근 가능
permitAll- 항상 true로 평가
denyAll- 항상 false로 평가
isAnonymous()- 현재 보안 주체가 익명 사용자면 true 반환
isRememberMe()- 현재 보안 주체가 remember-me 사용자면 true 반환
isAuthenticated()- 사용자가 익명이 아닌 경우 true 반환
isFullyAuthenticated()- 사용자가 익명 사용자,
remember-me 사용자가 아니면 true 반환
hasPermission(Object target, Object permission)사용자가 target에 해당 permission 권한이 있으면 true 반환

ex) hasPermission(domainObject, ‘read’)
profile
자료 정리 블로그

0개의 댓글