새롭게 토이프로젝트를 시작하면서, 소셜 로그인 기능을 Oauth2로 네이버와 구글과 연계해 구현해보았다. 그동안 편하게 서비스를 이용해오면서, 어떻게 동작하는지에 대한 기술적인 호기심을 해결할 수 있었고 이를 기록하고자 한다.
해당 게시글은 다음 강의를 참고하여 작성했습니다.
https://www.youtube.com/watch?v=xsmKOo-sJ3c&list=PLJkjrxxiBSFALedMwcqDw_BPaJ3qqbWeB&ab_channel=%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9C%A0%EB%AF%B8
OAuth2란 ❓
Open Authorization의 약자로 제 3자 인증 방식으로, 신뢰할 수 있는 웹사이트에 등록되어 있는 회원 정보를 활용하여 서비스에 로그인을 하는 기능을 말한다. 이는 정확히 말하자면, 대신 로그인을 하는 게 아니고 사용자 정보를 위임하는 기술이다.

주의! 위 사진과 아래에서 설명하는 내용 중 Client는 우리가 일반적으로 알고 있는 프론트 단의 클라이언트가 아닌 우리가 이용하는 서비스다.
Authorization, Resource 서버를 네이버라고 예를 들어서 설명하겠습니다.(구글, 페이스북, 깃헙 다 될 수 있음)

기술 스택
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.7'
implementation 'org.springframework.boot:spring-boot-starter-web'
//Security && oauth2
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-oauth2-client'
//compileOnly('org.springframework.boot:spring-boot-starter-security')
//JWT
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
implementation'org.mariadb.jdbc:mariadb-java-client'
implementation 'org.springframework.session:spring-session-jdbc'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
SecurityConfig
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig {
private final CustomOAuth2UserService customOAuth2UserService;
private final CustomSuccessHandler customSuccessHandler;
private final JWTUtil jwtUtil;
@Bean
public SecurityFilterChain config(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception{
http
.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(Collections.singletonList("Set-Cookie"));
configuration.setExposedHeaders(Collections.singletonList("Authorization"));
return configuration;
}
}));
//csrf disable
http
.csrf((auth) -> auth.disable());
//Form 로그인 방식 disable
http
.formLogin((auth) -> auth.disable());
//Http Basic 인증 방식 disable
http
.httpBasic((auth) -> auth.disable());
//JWT 필터 추가
http
.addFilterAfter(new JWTFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);
//oauth2
http
.oauth2Login((oauth2) -> oauth2
.userInfoEndpoint((userInfoEndpointConfig -> userInfoEndpointConfig
.userService(customOAuth2UserService)))
.successHandler(customSuccessHandler)
);
//경로별 인가 작업
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/").permitAll()
.anyRequest().authenticated());
//세션 설정 : STATELESS
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
로그인 요청이 들어오면 oauth2로 redirect하고 성공하면 successhandler에서 jwt 토큰을 발행한다.
jwtfilter에서 jwt토큰을 검증하고 유효할 경우, SecurityContextHolder에 유저 정보를 저장한다.
다음 포스트에서 entity, dto와 successhandler, jwt 발행 클래스, jwtFilter를 구현하겠습니다.
잘보고갑니다