주니어 개발자 성장기

3. Spring Security - 인증 과정 이해하기 (1) 본문

예제/Session

3. Spring Security - 인증 과정 이해하기 (1)

Junpyo Lee 2023. 5. 27. 13:08

Overview

이번에는 Spring Security에서 일반 로그인(Form Login)을 구현하고, 로그인 요청이 필터를 거치며 어떻게 인증이 되는 지 그 과정을 정리하려고 한다.

  1. Why Form Login?
  2. Spring Security 로그인 과정 분석 (Feat. 템플릿 메서드 패턴)
  3. 유저 정보는 어떻게 불러오는가?

Why Form Login?

Spring Security 에서는 로그인 방식으로 3가지를 지원한다.

Reading the Username & Password :: Spring Security

  1. 폼 로그인
  • 일반적으로 쓰는 로그인의 형태로
  • 대체로 POST 메서드와 form-data 형식으로 아이디와 패스워드를 전송한다.
  • 도청의 위험성이 있기 때문에 반드시 SSL/TLS 계층과 함께 사용한다.
  • 표준 사양이 존재하지 않는다.
  • 최초 Spring Security 로그인 기능 사용시 Default 값이다.
  1. Basic 로그인
  • 헤더에 ID와 비밀번호를 붙여서 보내는 방식으로 암호화가 되지 않아 도청의 위험성이 있다.
  1. Digest 로그인
  • 도청 방지를 위해 MD5라는 알고리즘을 이용해 인증하는 방식
  • MD5가 보안에 취약하다.

참조: 11. HTTP를 이용한 인증방식 - BASIC, DIGEST, SSL, FORM

그래서 폼 로그인을 도입 할만한 이유는?

  • Spring Security 에서 기본으로 제공하는 방식이다.
  • SSL 인증서만 잘 적용한다면 보안은 문제가 없다고 한다.
  • 클라이언트에서 어떤 식으로 요청하면 되는 지 내가 잘알고 있다.
  • 나는 SSL 인증서 발급받는 법을 알고 있다.

단점은?

  • 실 서비스에서 아이디, 비밀번호 찾기 등을 따로 구현해야 한다.
  • 비밀번호 관리, 보안에 힘써야 한다.
  • 상기 사항 때문에 소셜 로그인을 도입 했을 때에 비해 기회비용이 크다.

Spring Security Form Login의 흐름

Client가 로그인 요청(POST, URI: /login을 하면, 여러 필터들을 거쳐서 UsernamePasswordAuthenticationFilter에 도착하게 된다.

UsernamePasswordAuthenticationFilter

필터링을 시도하는 AbstractAuthenticationProcessingFilter.doFilter

메서드를 우선 살펴보자.

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
		throws IOException, ServletException {
	if (!requiresAuthentication(request, response)) {
		chain.doFilter(request, response);
		return;
	}
	try {
		Authentication authenticationResult = attemptAuthentication(request, response);
		if (authenticationResult == null) {
			// return immediately as subclass has indicated that it hasn't completed
			return;
		}
		//...
		successfulAuthentication(request, response, chain, authenticationResult);
		//...
	}
}

 

attemptAuthentication(request, response); 라는 추상 메서드를 호출하는 것을 볼 수 있다. (이것은 subclass에 위임하고 있다) 그리고 Authentication라는 인스턴스를 반환한다. Authentication이란 인증 정보를 담고 있는 인터페이스이며, 뒤쪽에서 AuthorizationFilter를 거치며 요청의 인증인가를 결정한다. 편의상 인증 토큰으로 명명하겠다. (그리고 인증이 끝나면 Security Context Holder에 저장된다.)

 

 

어쨌든, 다시 말하자면 인증의 세부 구현은 UsernamePasswordAuthenticationFilter에 위임하는 템플릿 메서드 패턴으로 구현되어 있는 것이다. 그러면 인증의 세부 구현을 파악하기 위해

UsernamePasswordAuthenticationFilter.attemptAuthentication을 살펴보자.

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
		throws AuthenticationException {
	// (1 )postOnly == true AND HTTP method 가 POST가 아니면 예외
	if (this.postOnly && !request.getMethod().equals("POST")) {
		throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
	}

	// (2) username과 password를 request로부터 가져온다.
	String username = obtainUsername(request);
	username = (username != null) ? username.trim() : "";
	String password = obtainPassword(request);
	password = (password != null) ? password : "";

	// (3) username과 password를 통해 인증되지 않은 인증 토큰 생성
	UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
			password);
	// Allow subclasses to set the "details" property
	// (4) 인증 토큰에 request로 부터 Detail 정보를 가져온다.
	setDetails(request, authRequest);

	// (5) AuthenticationManager 객체에 인증을 위임한다.
	return this.getAuthenticationManager().authenticate(authRequest);
}

 

  1. postOnly값은 디폴트로 true인데, request의 HTTP method가 POST가 아니라면 예외를 던진다.
  1. 그리고 request로부터 username(사용자 ID)과 password를 가져온다.
  1. username과 password를 담은 UsernamePasswordAuthenticationToken을 생성한다. 이 인증 토큰Authentication을 상속 받고 있으며 아직 UnAuthenticated(인증이 되지 않은 상태)이다.
  1. request로부터 인증 토큰에 들어갈 Details 필드를 가져온다. 어떻게 가져오는 지는 authenticationDetailsSource에 위임하고 있다. (전략 패턴)
  1. 필드에 있는 AuthenticationManager인증 토큰의 인증을 위임한다. 이 역시도 전략 패턴으로 구성되어 있다.

위에서 가장 중요한 부분은 (5)번이다. AuthenticationManager는 인증의 세부사항을 구현하는 인터페이스다. 인증 토큰을 전달 받고 완전한(인증된) 인증 토큰을 반환하는 책임을 가지고 있다.

ProviderManager

Spring Security에서는 AuthenticationManager의 구현체로 ProviderManager를 사용한다. ProviderManager는 필드로 List<AuthenticationProvider>를 갖고 있으며 반복문을 돌면서 해당 인증 토큰AuthenticationProvider 가 지원하는 지 찾은 다음, 인증을 시도한다.

 

 

ProviderManager이후의 자세한 내용은 다음 포스팅에 올리도록 하겠다.

'예제 > Session' 카테고리의 다른 글

4. Spring Security - 인증 과정 이해하기 (2)  (0) 2023.06.15
2. Spring Session  (0) 2023.05.18
1. 설정  (0) 2023.05.16
0. 프로젝트 목적  (0) 2023.05.16