[Security] 스프링 시큐리티 - Form Login의 작동 방식

오형상·2024년 9월 22일
0

Spring Security

목록 보기
1/1
post-thumbnail

커스텀 JSON 으로 로그인을 구현하면서 Spring Security에서 제공하는 Form Login에 대해 알아보아겠다고 생각되어 블로그에 정리하고자 합니다.

Spring Security Form Login 전체 흐름

1. 사용자가 로그인 요청 (POST /login):

  • 사용자가 웹 애플리케이션의 로그인 폼에 usernamepassword를 입력하고, /login 경로로 POST 요청을 보냅니다.
  • 이 요청은 Spring Security에서 제공하는 UsernamePasswordAuthenticationFilter에 의해 가로채집니다.
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = 
       new AntPathRequestMatcher("/login", "POST");

2. UsernamePasswordAuthenticationToken 객체 생성 후 AuthenticationManager로 전달

  • authRequestUsernamePasswordAuthenticationToken으로, 이 토큰은 인증되지 않은 상태로 생성됩니다.
  • this.getAuthenticationManager().authenticate(authRequest)를 호출하여 AuthenticationManager에게 토큰을 전달하고 인증을 시도합니다.
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    // 유저네임과 패스워드 추출
    String username = this.obtainUsername(request);
    String password = this.obtainPassword(request);
    
    // username과 password로 인증되지 않은 UsernamePasswordAuthenticationToken 생성
    UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
    
    // 인증 세부 사항 설정 (추가적인 요청 정보 포함)
    this.setDetails(request, authRequest);
    
    // AuthenticationManager에게 authRequest 전달하여 인증 진행
    return this.getAuthenticationManager().authenticate(authRequest);
}

3. AuthenticationManager가 적절한 AuthenticationProvider를 사용하여 인증 진행

  • AuthenticationManager는 내부적으로 다양한 AuthenticationProvider를 사용하여 인증을 처리합니다. 주로 DaoAuthenticationProviderUserDetailsService를 사용해 데이터베이스에서 사용자 정보를 가져와 비밀번호를 검증하는 방식으로 인증을 처리합니다.
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder());  // PasswordEncoder 설정
        return provider;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // AuthenticationManager에 DaoAuthenticationProvider 등록
        auth.authenticationProvider(daoAuthenticationProvider());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

4. DaoAuthenticationProvider에서 인증 처리:

  • DaoAuthenticationProviderUserDetailsService를 사용해 username에 해당하는 사용자 정보를 데이터베이스나 다른 저장소에서 로드합니다.

  • DaoAuthenticationProvider는 사용자가 입력한 raw password와 UserDetails에 저장된 암호화된 password를 PasswordEncoder를 사용해 비교합니다.

  • 비밀번호가 일치하면, DaoAuthenticationProvider는 principal로 UserDetails 객체를, authorities로 사용자의 권한 정보를 담은 새로운 UsernamePasswordAuthenticationToken 객체(인증된 상태)를 생성합니다.

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
     // authentication 객체에서 입력된 username 가져오기
     String username = authentication.getName();
    
     // UserDetailsService를 통해 username에 해당하는 사용자 정보 로드
     UserDetails userDetails = userDetailsService.loadUserByUsername(username);
     
     // 입력된 비밀번호와 데이터베이스에 저장된 비밀번호 비교
     if (userDetails == null || !passwordEncoder.matches(authentication.getCredentials().toString(), userDetails.getPassword())) {
         throw new BadCredentialsException("Invalid username or password");
     }
    
     // 인증 성공: 인증된 UsernamePasswordAuthenticationToken 반환
     return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }
    

5. ProviderManager에서 인증 객체 반환:

  • ProviderManager인증된 UsernamePasswordAuthenticationToken 객체를 반환합니다.
  • 이 객체는 이후 애플리케이션에서 인증된 사용자 정보로 사용됩니다.

0개의 댓글