이번 게시글에선 저번에 분석한 스프링 시큐리티를 바탕으로 커스텀 구현을 해보겠습니다.
목표는 기존 폼 형식 로그인 방식을 Jwt 로그인 방식으로 변경하는 것입니다.
저번에 분석했던 스프링 시큐리티의 폼 로그인 흐름을 간략히 정리해볼까요?
- UsernamePasswordAuthenticationFilter
- 폼 요청으로 미인증된 Authentication 생성, AuthenticationManager에 미인증 Authentication 전달
- AuthenticationManager - ProviderManager
- AuthenticationProvider에 미인증 Authentication 전달
- AuthenticationProvider - DaoAuthenticationProvider
- UserDetailsService와 PasswordEncoder로 UserDetails 생성
- 인증된 Authentication 생성
- SecurityContext에 인증된 Authentication 등록
위 중에서 커스텀 구현할 클래스는 아래입니다.
- AbstractAuthenticationProcessingFilter
- UserDetailService
@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final ObjectMapper objectMapper;
private final UserRepository userRepository;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests()
.requestMatchers("/auth/login").permitAll()
.requestMatchers("/auth/signup").permitAll()
.anyRequest().authenticated()
.anyRequest().permitAll()
.and()
.addFilterBefore(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) //json 로그인 처리 필터 삽입
.csrf(AbstractHttpConfigurer::disable)
.build();
}
@Bean
public EmailPasswordAuthFilter usernamePasswordAuthenticationFilter() {
EmailPasswordAuthFilter filter = new EmailPasswordAuthFilter("/auth/login", objectMapper, jwtTokenProvider);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(new LoginSuccessHandler());
filter.setAuthenticationFailureHandler(new LoginFailHandler(objectMapper));
// 실제로 인증이 완료 됐을 때 요청 내에서 인증이 유효하도록 만들어주는 컨텍스트 -> 이것이 있어야 세션 발급됨
filter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());
return filter;
}
@Bean
public AuthenticationManager authenticationManager() { //filter에 AuthenticationManager 넘겨주기 위한 Bean
return new ProviderManager(authenticationProvider()); //provider를 넘겨주기
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService(userRepository));
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public UserDetailsService userDetailsService(UserRepository userRepository)
// DB로 관리하기
return username -> {
User user = userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException(username + "을 찾을 수 없습니다."));
return new UserPrincipal(user);
};
}
@Bean
public PasswordEncoder passwordEncoder() {
return new SCryptPasswordEncoder(
16,
8,
1,
32,
64);
}
}
// json 요청 방식으로 로그인하기 위한 Filter 커스텀 구현
public class EmailPasswordAuthFilter extends AbstractAuthenticationProcessingFilter {
private final ObjectMapper objectMapper;
public EmailPasswordAuthFilter(String loginUrl, ObjectMapper objectMapper) {
super(loginUrl);
this.objectMapper = objectMapper;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
EmailPassword emailPassword = objectMapper.readValue(request.getInputStream(), EmailPassword.class);
//todo 토큰 커스텀 해보기..
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(
emailPassword.email,
emailPassword.password
);
token.setDetails(this.authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(token);
}
@Getter
private static class EmailPassword {
private String email;
private String password;
}
}
'백엔드 > Spring Security' 카테고리의 다른 글
스프링 시큐리티 json 형식으로 로그인하기 - 3. jwt로 구현 (1) | 2024.06.12 |
---|---|
스프링 시큐리티 json 형식으로 로그인하기 - 1. 스프링 시큐리티 UsernamePasswordAuthenticationFilter 분석 (0) | 2024.05.10 |