스프링 시큐리티 로그인 기능 구현

스프링 시큐리티는 스프링 기반의 애플리케이션 보안을 담당하는 프레임워크입니다. 이를 이용하여 로그인 기능을 구현할 수 있습니다.

로그인 기능을 구현하기 위해서는 스프링 시큐리티에서 제공하는 AuthenticationManager와 UserDetailsService를 구현해야 합니다. AuthenticationManager는 인증(Authentication)을 담당하고, UserDetailsService는 사용자 정보를 가져오는 역할을 합니다.

또한, 로그인 페이지와 로그인 성공/실패 시의 처리를 위한 컨트롤러와 뷰를 구현해야 합니다. 로그인 폼에서 입력한 아이디와 비밀번호를 AuthenticationManager에 전달하여 인증을 수행하고, 인증 결과에 따라 로그인 성공/실패를 처리합니다.


먼저 스프링시큐리티의 설정 코드를 작성해줍니다.

SecurityConfig

@Configuration
public class SecurityConfig {

    @Autowired
    private PrincipalDetailService principalDetailService;

    @Bean
    public BCryptPasswordEncoder encodePWD(){
        return new BCryptPasswordEncoder();
    }

    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.csrf().disable() // csrf 토큰 비활성화 (테스트시 걸어두는 게 좋음)

                .authorizeRequests()
                    .antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**")
                    .permitAll()
                    .anyRequest()
                    .authenticated()

                .and()
                    .formLogin()
                    .loginPage("/auth/loginForm")
                    .loginProcessingUrl("/auth/loginProc")
                    .defaultSuccessUrl("/");

        return http.build();
    }
}

해당 서비스의 회원가입 로직에서 비밀번호는 BCryptPasswordEncoder에 의해 해시 암호화 되므로 해당 객체를 빈에 등록해줍니다.

SecurityFilterChain을 사용하여 필요한 권한을 설정할 수 있습니다.

위의 예제는 모든 사용자(로그인 하지 않은 사용자 포함)가 "/", "/auth/**", "/js/**", "/css/**", "/image/**" 의 접근을 가능하게 하며 스프링 시큐리티 기반 login을 "/auth/loginForm" 경로에서 진행되게 합니다. 로그인 Form Action Url은 "/auth/loginProc" 경로가 됩니다.

PrincipalDetailService

@Service
public class PrincipalDetailService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User principal = userRepository.findByUsername(username)
                .orElseThrow(()->{
                    return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. " + username);
                });

        return new PrincipalDetail(principal); // 시큐리티 세션에 유저 정보가 저장이 됨
    }

}

UserDetailsService 인터페이스를 구현하는 서비스입니다. 스프링 시큐리티에서 사용자 인증 처리를 위해 UserDetailsService를 이용합니다. loadUserByUsername 메서드를 오버라이딩하여 사용자 정보를 가져옵니다.

userRepository.findByUsername(username) 메서드를 이용해 해당 username을 가진 사용자 정보를 가져옵니다. 사용자 정보가 존재하지 않으면 UsernameNotFoundException을 던집니다.

PrincipalDetail 객체를 생성하여 사용자 정보를 인자로 전달합니다. PrincipalDetail 객체는 UserDetails 인터페이스를 구현한 클래스로, 시큐리티 세션에 저장됩니다.

PrincipalDetail