본문 바로가기

Language/Spring Security

폼 인증 - formLogin()

728x90

폼 인증

  • HTTP 기반의 폼 로그인 인증 메커니즘을 활성화하는 API
  • 사용자 인증을 위한 사용자 정의 로그인 페이지 쉽게 구현 가능
  • 기본적으로 스프링 시큐리티가 제공하는 기본 로그인 페이지 사용
  • 사용자 이름과 비밀번호 필드가 포함된 간단한 로그인 양식 제공
  • 사용자는 웹 폼을 통해 자격 증명(ID,PW)을 제공하고 Spring Security는 HttpServletRequest에서 이 값을 읽어옴

폼 인증 흐름

  1. client가 URL 요청 (/user)
  2. 권한 검사 필터(AuthorizationFilter)가 사용자가 /user 경로에 접근이 가능한 지 검사
  3. 해당 클라이언트가 인증을 받지 못했을 경우 접근 예외(AccessDeniedException) 발생
  4. 예외 처리 필터(ExceptionTranslationFilter)가 예외를 받음
    → 접근을 하려는 자원이 인증을 필요로 할 때 로그인 페이지로 이동 시키는 역할
  5. 인증 시작(AuthenticationEntryPoint)이 로그인 페이지로 리다이렉트
  6. 로그인 성공 시 서버로 접근 가능

formLogin() API

  • FormLoginConfigurer 설정 클래스를 통해 여러 API 설정 가능
  • 내부적으로 UsernamePasswordAuthenticationFilter가 생성되어 폼 인증 처리를 담당
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .formLogin(form -> form
                    .loginPage("/loginPage")
                    .loginProcessingUrl("/loginProc")
                    .defaultSuccessUrl("/", true)
                    .failureUrl("/failed")
                    .usernameParameter("userId")
                    .usernameParameter("passwd")
                    .successHandler((request, response, authentication) -> {
                        System.out.println("authentication :" + authentication);
                        response.sendRedirect("/home");
                    })
                    .failureHandler((request, response, exception) -> {
                        System.out.println("exception : " + exception.getMessage());
                        response.sendRedirect("/login");
                    })
                    .permitAll()
            );
    return http.build();
}
Method 설명
.loginPage("/loginPage") 사용자 정의 로그인 페이지 지정, 기본 로그인 페이지 무시
→ 없을 경우 기본 로그인 페이지 제공
.loginProcessingUrl("/loginProc") 사용자 이름과 비밀번호를 검증할 URL 지정
→ 프론트에서 <form action="URL">과 동기화
.defaultSuccessUrl("/", true) 로그인 성공 이후 이동 페이지, true 지정시 무조건 해당 페이지로 이동
→  지정하지 않거나 false일 경우 원래 방문하려던 페이지로 리다이렉트
.failureUrl("/failed") 인증 실패(로그인이 되지 않았을 경우) 시 보내질 URL
→ 기본값 = /login?error
.usernameParameter("userId") 인증에 쓰일 id 파라미터명, 기본값 username
→ 프론트의 id <input> 태그와 이름 동기화
.usernameParameter("passwd") 인증에 쓰일 pw 파라미터명, 기본값 username
→ 프론트의 pw <input> 태그와 이름 동기화
.successHandler(AuthenticationSuccessHandler) 인증 성공 시 사용할 SuccessHandler 지정
.faliureHandler(AuthenticationFailureHandler) 인증 실패 시 사용할 FailureHandler 지정
.permitAll() loginPage(), failureUrl(),loginProcessingUrl()에 대한 URL에 모든 사용자 접근 허용
로그인이 되지 않은 사용자에게 인증을 할 수 없기 때문

Controller 및 formLogin 수정

  • IndexController
@RestController
public class IndexController {
    @GetMapping("/")
    public String index() {
        return "index";
    }

    @GetMapping("/home")
    public String home(){
        return "home";
    }

    @GetMapping("/loginPage")
    public String loginPage(){
        return "loginPage";
    }
}
  • SecurityConfig
@EnableWebSecurity
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                .formLogin(form -> form
                        .loginPage("/loginPage")
                        .loginProcessingUrl("/loginProc")
                        .defaultSuccessUrl("/", true)
                        .failureUrl("/failed")
                        .usernameParameter("userId")
                        .usernameParameter("passwd")
                        .successHandler((request, response, authentication) -> {
                            System.out.println("authentication :" + authentication);
                            response.sendRedirect("/home");
                        })
                        .failureHandler((request, response, exception) -> {
                            System.out.println("exception : " + exception.getMessage());
                            response.sendRedirect("/login");
                        })
                        .permitAll()
                );
        return http.build();
    }
    @Bean
    public UserDetailsService userDetailsService(){
        UserDetails user = User.withUsername("user")
                .password("{noop}1111")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
}

테스트

  • 위 코드 그대로 실행
    • localhost:8080 접속
    • 기본 로그인 페이지 무시된 것 확인

  • loginPage(), successHandler(), failureHandler() 주석 처리 후 재실행
    • 기본 로그인 페이지 확인
    • 로그인 시 localhost:8080 이동
    • 실패 시 localhost:8080/failed 이동

  • successHandler(), failureHandler() 주석 해제 후 재 실행
    • 로그인 성공 시 localhost:8080/home 이동
    • 로그인 실패 시 localhost:8080/login 이동
    • ⇒ ??Handler()가 defaultSuccessUrl과 failureURL보다 우선 순위를 가짐

728x90