Spring

[Spring] Oauth 연동 - API 로그인 기능 구현하기

obin01 2025. 1. 20. 17:51

1. SecurityConfig

package oauth.core.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import lombok.RequiredArgsConstructor;
import oauth.core.filter.CustomLoginFilter;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
	
	private final AuthenticationConfiguration authenticationConfiguration;
	
	@Bean
	SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		
		AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
		CustomLoginFilter customLoginFilter = new CustomLoginFilter(authenticationManager);
		customLoginFilter.setFilterProcessesUrl("/api/login");
		
		http
			.securityMatcher("/api/**")
			.csrf(AbstractHttpConfigurer::disable)
			.formLogin(AbstractHttpConfigurer::disable)
			.authorizeHttpRequests(authz -> authz
				.requestMatchers("/api/login").permitAll()
				.anyRequest().authenticated()
			)
			.addFilterAt(customLoginFilter, UsernamePasswordAuthenticationFilter.class);
		
		return http.build();
	}
}

📌 SecurityConfig 추가 정리

securityMatcher("/api/**") : " /api/** "로 시작하는 요청에 대해 필터 체인이 작동하도록 제한
csrf(AbstractHttpConfigurer::disable) : CSRF 비활성화

formLogin(AbstractHttpConfigurer::disable) : 폼 로그인 비활성화
requestMatchers("/api/login").permitAll() : " /api/login "로 시작하는 요청만 인증 없이 접근 허용
anyRequest.authenticated() : 정의된 요청 경로 이외의 모든 요청은 인증된 사용자만 접근 가능

customLoginFilter.setFilterProcessesUrl("/api/login") : 커스텀 필터에 거치게 할 url 등록

addFilterAt(customLoginFilter, UsernamePasswordAuthenticationFilter.class) : 해당 필터를 커스텀으로 대체함

2. CustomLoginFilter

package oauth.core.filter;

import java.io.IOException;
import java.util.Map;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class CustomLoginFilter extends UsernamePasswordAuthenticationFilter {

	private final AuthenticationManager authenticationManager;

	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		String username = obtainUsername(request);
		String password = obtainPassword(request);

		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

		return authenticationManager.authenticate(authRequest);
	}

	@Override
	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
			Authentication authentication) throws JsonProcessingException, IOException {

		ObjectMapper objectMapper = new ObjectMapper();

		response.setContentType("application/json");
		response.getWriter().write(objectMapper
				.writeValueAsString(Map.of(
						"message", 
						"Authentication successful", 
						"user", 
						authentication.getPrincipal()
				)));
	}

	@Override
	protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException failed) throws JsonProcessingException, IOException {

		ObjectMapper objectMapper = new ObjectMapper();

		response.setContentType("application/json");
		response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		response.getWriter().write(objectMapper
				.writeValueAsString(Map.of(
						"message", 
						"Authentication failed", 
						"error", 
						failed.getMessage()
				)));
	}
}

📌 CustomLoginFilter 추가 정리

attemptAuthentication() : 사용자 인증 처리 부분

successfulAuthentication() : 인증 성공시 처리되는 부분

unsuccessfulAuthentication() : 인증 실패시 처리되는 부분

obtainUsername() : 요청에서 username으로 오는 파라미터를 가져옴

obtainPassword() : 요청에서 password으로 오는 파라미터를 가져옴