Contents
see List개요
Spring Security 6.x는 Lambda DSL 기반의 새로운 설정 방식을 도입하며 OAuth2와 OpenID Connect(OIDC) 구현을 크게 간소화했습니다. 기존의 WebSecurityConfigurerAdapter 상속 방식이 완전히 제거되고, SecurityFilterChain 빈 등록 방식으로 일원화되었습니다. 이 글에서는 Google, GitHub 소셜 로그인부터 JWT 기반 리소스 서버 구축까지 실전 구현 과정을 다룹니다.
핵심 개념
OAuth2 Login은 사용자 인증을 외부 Identity Provider에 위임하는 방식입니다. Spring Security는 Authorization Code Grant 흐름을 자동으로 처리하며, 개발자는 최소한의 설정만으로 소셜 로그인을 구현할 수 있습니다.
OIDC(OpenID Connect)는 OAuth2 위에 구축된 인증 레이어로, ID Token을 통해 사용자 정보를 표준화된 방식으로 제공합니다. Spring Security는 OIDC Discovery를 자동으로 처리합니다.
Resource Server는 JWT 토큰을 검증하여 API를 보호하는 역할을 합니다. Spring Security 6.x에서는 oauth2ResourceServer() 설정으로 JWT 또는 Opaque Token 검증을 구성합니다.
실전 예제
Google과 GitHub 소셜 로그인을 설정합니다.
# application.yml
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
scope: openid, profile, email
github:
client-id: ${GITHUB_CLIENT_ID}
client-secret: ${GITHUB_CLIENT_SECRET}
scope: read:user, user:email
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/public/**", "/css/**", "/js/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.userInfoEndpoint(userInfo -> userInfo
.userService(customOAuth2UserService())
.oidcUserService(customOidcUserService())
)
.successHandler(oAuth2SuccessHandler())
)
.oauth2ResourceServer(resource -> resource
.jwt(jwt -> jwt
.jwtAuthenticationConverter(jwtAuthConverter())
)
)
.csrf(csrf -> csrf
.ignoringRequestMatchers("/api/**")
)
.build();
}
@Bean
public JwtAuthenticationConverter jwtAuthConverter() {
JwtGrantedAuthoritiesConverter authConverter =
new JwtGrantedAuthoritiesConverter();
authConverter.setAuthorityPrefix("ROLE_");
authConverter.setAuthoritiesClaimName("roles");
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(authConverter);
return converter;
}
}
커스텀 OAuth2 User Service로 사용자 정보를 DB에 저장합니다.
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest request)
throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(request);
String provider = request.getClientRegistration().getRegistrationId();
String providerId = oAuth2User.getAttribute("id").toString();
String email = oAuth2User.getAttribute("email");
String name = oAuth2User.getAttribute("name");
User user = userRepository.findByProviderAndProviderId(provider, providerId)
.orElseGet(() -> userRepository.save(
User.builder()
.provider(provider)
.providerId(providerId)
.email(email)
.name(name)
.role(Role.USER)
.build()
));
return new CustomOAuth2User(user, oAuth2User.getAttributes());
}
}
활용 팁
- 토큰 저장 전략: 서버 사이드 세션 대신 JWT를 사용하면 Stateless 아키텍처를 유지할 수 있습니다. Redis에 Refresh Token을 저장하는 패턴이 일반적입니다.
- CORS 설정: SPA 프론트엔드와 연동 시
CorsConfigurationSource빈을 반드시 등록하세요. - Method Security:
@PreAuthorize("hasRole('ADMIN')")어노테이션으로 메서드 레벨 권한 제어가 가능합니다. - 테스트:
@WithMockUser,@WithMockOAuth2User어노테이션으로 인증 상태를 모킹하여 테스트할 수 있습니다. - Keycloak 연동: 사내 SSO 구축 시 Keycloak을 OIDC Provider로 사용하면 별도 코드 없이 application.yml 설정만으로 연동됩니다.
마무리
Spring Security 6.x의 OAuth2/OIDC 지원은 과거에 비해 설정이 직관적이고 확장성도 뛰어납니다. Lambda DSL 패턴에 익숙해지면 복잡한 인증/인가 시나리오도 깔끔하게 구현할 수 있습니다. 소셜 로그인으로 시작하여 점진적으로 JWT Resource Server, Method Security 등을 추가하는 방식으로 접근하시기 바랍니다.