Spring Security ‐ Username password‐based authentication mechanism - taeyun-ham/andalos GitHub Wiki
사용자를 인증하는 가장 일반적인 방법 중 하나는 사용자 이름과 비밀번호를 검증하는 것입니다. 스프링 시큐리티는 사용자 이름과 비밀번호를 사용한 인증에 대해 포괄적인 지원을 제공합니다.
Spring Security는 HttpServletRequest에서 사용자 이름과 비밀번호를 읽기 위해 다음과 같은 내장 메커니즘을 제공합니다.
- Form
- Basic
- Digest

스프링 시큐리티는 HTML 폼을 통해 제공되는 사용자 이름과 비밀번호에 대한 지원을 제공합니다. 이 섹션에서는 스프링 시큐리티 내에서 폼 기반 인증이 어떻게 작동하는지에 대한 세부 정보를 제공합니다.
이 섹션에서는 스프링 시큐리티 내에서 폼 기반 로그인이 어떻게 작동하는지 살펴봅니다. 먼저, 사용자가 로그인 폼으로 어떻게 리다이렉트되는지 확인합니다:

앞서 언급된 그림은 우리의 SecurityFilterChain 다이어그램을 바탕으로 합니다.
- 먼저, 사용자가 권한이 없는 리소스(/private)에 대해 인증되지 않은 요청을 합니다.
- 스프링 시큐리티의 AuthorizationFilter는 인증되지 않은 요청이 거부되었음을 나타내기 위해 AccessDeniedException을 발생시킵니다.
- 사용자가 인증되지 않았기 때문에, ExceptionTranslationFilter는 Start Authentication을 시작하고 구성된 AuthenticationEntryPoint를 사용하여 로그인 페이지로 리다이렉트를 보냅니다. 대부분의 경우, AuthenticationEntryPoint는 LoginUrlAuthenticationEntryPoint의 인스턴스입니다.
- 브라우저가 리다이렉트된 로그인 페이지를 요청합니다.
- 애플리케이션 내의 무언가가 로그인 페이지를 렌더링해야 합니다.
사용자 이름과 비밀번호가 제출되면, UsernamePasswordAuthenticationFilter는 사용자 이름과 비밀번호를 인증합니다. UsernamePasswordAuthenticationFilter는 AbstractAuthenticationProcessingFilter를 확장하므로, 다음 다이어그램은 상당히 유사해 보일 것입니다:

이 그림은 우리의 SecurityFilterChain 다이어그램을 바탕으로 합니다.
- 사용자가 사용자 이름과 비밀번호를 제출하면, UsernamePasswordAuthenticationFilter는 HttpServletRequest 인스턴스에서 사용자 이름과 비밀번호를 추출하여 UsernamePasswordAuthenticationToken을 생성합니다. UsernamePasswordAuthenticationToken은 Authentication의 한 유형입니다.
- 다음으로, UsernamePasswordAuthenticationToken은 인증을 위해 AuthenticationManager 인스턴스로 전달됩니다. 사용자 정보가 어떻게 저장되어 있는지에 따라 AuthenticationManager의 세부 사항이 달라집니다.
- 인증에 실패하면, 실패입니다.
-
- SecurityContextHolder가 비워집니다.
-
- RememberMeServices.loginFail이 호출됩니다. 리멤버 미(remember me)가 구성되지 않은 경우, 이는 작업 없음입니다. Javadoc에서 RememberMeServices 인터페이스를 참조하세요.
-
- AuthenticationFailureHandler가 호출됩니다. Javadoc에서 AuthenticationFailureHandler 클래스를 참조하세요.
- 인증에 성공하면, 성공입니다.
-
- SessionAuthenticationStrategy가 새로운 로그인에 대해 알림을 받습니다. Javadoc에서 SessionAuthenticationStrategy 인터페이스를 참조하세요.
-
- Authentication이 SecurityContextHolder에 설정됩니다. Javadoc에서 SecurityContextPersistenceFilter 클래스를 참조하세요.
-
- RememberMeServices.loginSuccess가 호출됩니다. 리멤버 미가 구성되지 않은 경우, 이는 작업 없음입니다. Javadoc에서 RememberMeServices 인터페이스를 참조하세요.
-
- ApplicationEventPublisher가 InteractiveAuthenticationSuccessEvent를 발행합니다.
-
- AuthenticationSuccessHandler가 호출됩니다. 일반적으로 이는 SimpleUrlAuthenticationSuccessHandler이며, 로그인 페이지로 리다이렉트할 때 ExceptionTranslationFilter에 의해 저장된 요청으로 리다이렉트합니다.
기본적으로 스프링 시큐리티 폼 로그인이 활성화되어 있습니다. 그러나, 서블릿 기반의 구성이 제공되는 즉시, 폼 기반 로그인은 명시적으로 제공되어야 합니다. 다음 예시는 최소한의 명시적 자바 구성을 보여줍니다:
Form Login
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.formLogin(withDefaults());
// ...
}Login Form - src/main/resources/templates/login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Please Log In</title>
</head>
<body>
<h1>Please Log In</h1>
<div th:if="${param.error}">
Invalid username and password.</div>
<div th:if="${param.logout}">
You have been logged out.</div>
<form th:action="@{/login}" method="post">
<div>
<input type="text" name="username" placeholder="Username"/>
</div>
<div>
<input type="password" name="password" placeholder="Password"/>
</div>
<input type="submit" value="Log in" />
</form>
</body>
</html>기본 HTML 양식에 대한 몇 가지 핵심 사항이 있습니다.
- form 은 /login으로 post를 수행해야 합니다.
- form 에는 CSRF 토큰이 포함되어야 합니다. 이는 Thymeleaf에 의해 자동으로 포함됩니다.
- form 은 username이라는 이름의 매개변수로 사용자 이름을 지정해야 합니다.
- form 은 password라는 이름의 매개변수로 비밀번호를 지정해야 합니다.
- error라는 이름의 HTTP 매개변수가 발견되면, 사용자가 유효한 사용자 이름이나 비밀번호를 제공하지 못했음을 나타냅니다.
- logout이라는 이름의 HTTP 매개변수가 발견되면, 사용자가 성공적으로 로그아웃했음을 나타냅니다.
추가되는 Filters
- UsernamePasswordAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- LogoutFilter
위 Filter 가 적용되는 절차를 보려면 FormLoginConfigurer 를 보면 됩니다.
Custom Login Form Configuration
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
// ...
}추가되는 Filters
- UsernamePasswordAuthenticationFilter
- LogoutFilter

이 섹션에서는 스프링 시큐리티가 서블릿 기반 애플리케이션을 위한 기본 HTTP 인증을 지원하는 방법에 대한 세부 정보를 제공합니다.
이 섹션에서는 스프링 시큐리티 내에서 HTTP 기본 인증이 어떻게 작동하는지 설명합니다. 먼저, WWW-Authenticate 헤더가 인증되지 않은 클라이언트에게 다시 전송되는 것을 볼 수 있습니다:

앞서 언급된 그림은 우리의 SecurityFilterChain 다이어그램을 기반으로 합니다.
- 먼저, 사용자가 권한이 없는 리소스 /private에 대해 인증되지 않은 요청을 합니다.
- 스프링 시큐리티의 AuthorizationFilter는 인증되지 않은 요청이 거부되었음을 나타내기 위해 AccessDeniedException을 발생시킵니다.
- 사용자가 인증되지 않았기 때문에, ExceptionTranslationFilter는 Start Authentication을 시작합니다. 구성된 AuthenticationEntryPoint는 BasicAuthenticationEntryPoint의 인스턴스로, WWW-Authenticate 헤더를 전송합니다. RequestCache는 일반적으로 NullRequestCache로, 클라이언트가 원래 요청한 요청을 다시 재생할 수 있으므로 요청을 저장하지 않습니다.
클라이언트가 WWW-Authenticate 헤더를 받으면, 사용자 이름과 비밀번호로 다시 시도해야 함을 알게 됩니다. 다음 이미지는 사용자 이름과 비밀번호가 처리되는 흐름을 보여줍니다:

앞서 언급된 그림은 우리의 SecurityFilterChain 다이어그램을 기반으로 합니다.
- 사용자가 사용자 이름과 비밀번호를 제출할 때, BasicAuthenticationFilter는 HttpServletRequest에서 사용자 이름과 비밀번호를 추출하여 UsernamePasswordAuthenticationToken을 생성합니다. 이는 Authentication의 한 유형입니다.
- 다음으로, UsernamePasswordAuthenticationToken은 인증을 위해 AuthenticationManager로 전달됩니다. AuthenticationManager의 구체적인 모습은 사용자 정보가 어떻게 저장되어 있는지에 따라 달라집니다.
- 인증에 실패하면, 실패입니다.
-
- SecurityContextHolder가 비워집니다.
-
- RememberMeServices.loginFail이 호출됩니다. 리멤버 미(remember me)가 구성되지 않은 경우, 이는 작업 없음입니다. Javadoc에서 RememberMeServices 인터페이스를 참조하세요.
-
- AuthenticationEntryPoint가 호출되어 WWW-Authenticate가 다시 전송되도록 합니다. Javadoc에서 AuthenticationEntryPoint 인터페이스를 참조하세요. 인증에 성공하면, 성공입니다.
- Authentication이 SecurityContextHolder에 설정됩니다.
-
- RememberMeServices.loginSuccess가 호출됩니다. 리멤버 미가 구성되지 않은 경우, 이는 작업 없음입니다. Javadoc에서 RememberMeServices 인터페이스를 참조하세요.
-
- BasicAuthenticationFilter는 FilterChain.doFilter(request,response)를 호출하여 애플리케이션의 나머지 로직으로 계속 진행합니다. Javadoc에서 - 3. BasicAuthenticationFilter 클래스를 참조하세요.
기본적으로, 스프링 시큐리티의 HTTP 기본 인증 지원이 활성화되어 있습니다. 그러나, 서블릿 기반 구성이 제공되는 즉시, HTTP 기본은 명시적으로 제공되어야 합니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.httpBasic(withDefaults());
return http.build();
}추가되는 Filters
- BasicAuthenticationFilter
