본문 바로가기
spring/spring security

[Spring Security5] OAuth2 로그인 설정

by moonsiri 2024. 10. 17.
728x90
반응형

Spring Boot 애플리케이션에 OAuth 로그인을 추가하는 과정은 주로 Spring SecurityOAuth2 클라이언트 설정을 통해 이루어집니다. OAuth 제공자(예: Google, Facebook, Naver 등)와 통합하기 위한 일반적인 순서는 다음과 같습니다

 

1. 필요한 의존성 추가

Spring Boot 프로젝트에서 OAuth2 로그인을 지원하려면 Spring Security OAuth2 Client 의존성을 추가해야 합니다.

Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

 

2. OAuth2 제공자 설정

OAuth2 로그인에 사용할 OAuth 제공자의 정보를 애플리케이션 설정 파일에 추가합니다. 예를 들어, Google을 사용하는 경우 아래와 같이 설정할 수 있습니다.

application.yml:

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: {client-id}
            client-secret: {client-secret}
            scope: profile, email
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            authorization-grant-type: authorization_code
            client-authentication-method: client_secret_basic
            client-name: Google
        provider:
          google:
            authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
            token-uri: https://oauth2.googleapis.com/token
            user-info-uri: https://openidconnect.googleapis.com/v1/userinfo
            user-name-attribute: sub

여기서, client-id, client-secret은 해당 OAuth 제공자의 개발자 콘솔에서 발급받아야 합니다.

spring.security.oauth2.client.registration.*.redirect-uri의 `baseUrl`과 `registrationId`Spring Security의 OAuth2 로그인 과정에서 자동으로 설정됩니다.

  • baseUrl : 애플리케이션이 http://localhost:8080에서 실행 중이라면, baseUrl은 http://localhost:8080이 됩니다.
  • registrationId : OAuth2 클라이언트 설정에서 각각의 OAuth 제공자를 식별하는 값입니다. (예: google, facebook, naver 등)

 

위 설정을 기반으로 ClientRegistration 객체를 생성하고, 이를 관리하는 ClientRegistrationRepository 빈을 자동으로 등록합니다.

  • 자동 생성 Bean
    • ClientRegistrationRepository: 여러 개의 클라이언트 등록 정보를 관리하는 리포지토리입니다. Spring Boot는 application.yml이나 application.properties에 설정된 내용을 바탕으로 이 빈을 자동으로 생성합니다.
    • OAuth2AuthorizedClientService: OAuth2 클라이언트와 관련된 인가된 클라이언트를 관리하는 서비스입니다. 액세스 토큰, 리프레시 토큰 등을 관리합니다.
    • OAuth2LoginAuthenticationFilter: OAuth2 로그인 필터로, 로그인 요청을 처리하는 필터입니다.

 

만약, application.yml이 아닌 java configuration으로 설정하고싶다면, 다음과 같은 방식으로 ClientRegistrationClientRegistrationRepository를 직접 등록할 수 있습니다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;

@Configuration
public class OAuth2ClientConfig {

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(this.googleClientRegistration());
    }

    private ClientRegistration googleClientRegistration() {
        return ClientRegistration.withRegistrationId("google")
            .clientId(clientId)  // Google OAuth 클라이언트 ID
            .clientSecret(clientSecret)  // Google OAuth 클라이언트 Secret
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)  // 인증 방식
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)  // 권한 부여 방식
            .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")  // 리디렉션 URI
            .scope("profile", "email")  // 요청하는 scope (사용자 정보 범위)
            .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")  // 권한 승인 URI
            .tokenUri("https://oauth2.googleapis.com/token")  // 토큰 발급 URI
            .userInfoUri("https://openidconnect.googleapis.com/v1/userinfo")  // 사용자 정보 조회 URI
            .userNameAttributeName(IdTokenClaimNames.SUB)  // 사용자 ID로 사용할 속성
            .clientName("Google")  // 클라이언트 이름
            .build();
    }
}

 

 

[SpringSecurity5] OAuth2.0로 네이버 로그인 구현

OAuth2.0로 네이버 로그인 구현 방법에 대해 포스팅하겠습니다. 1. 네이버 개발자 센터에서 애플리케이션 등록네이버 개발자 센터(https://developers.naver.com)에접속하여 로그인 후 애플리케이션을 등

moonsiri.tistory.com

 

[SpringSecurity5] OAuth2.0로 카카오 로그인 구현

OAuth2.0로 카카오 로그인 구현 방법에 대해 포스팅하겠습니다. 1. 카카오 개발자 센터에서 애플리케이션 등록카카오 개발자 센터(https://developers.kakao.com/)에 접속하여 로그인 후 애플리케이션을

moonsiri.tistory.com

 

 

3. Spring Security 설정

Spring Security 설정을 통해 OAuth2 로그인 기능을 활성화합니다.

import org.springframework.context.annotation.Bean;
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.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/login").permitAll()  // 로그인 페이지는 모두 접근 가능
                .anyRequest().authenticated()           // 그 외 모든 요청은 인증 필요
                .and()
            .oauth2Login()                              // OAuth2 로그인 활성화
                .loginPage("/login")                    // 커스텀 로그인 페이지 설정
                .defaultSuccessUrl("/home", true);      // 로그인 성공 시 리다이렉트 경로 설정
    }
}
  • oauth2Login(): OAuth2 로그인을 활성화.

 

4. OAuth 로그인 버튼 추가 (프론트엔드)

로그인 페이지에 OAuth 제공자로 로그인을 위한 링크를 추가합니다. Spring Security는 기본적으로 /oauth2/authorization/{registrationId} 경로를 사용하여 해당 OAuth 제공자를 통해 로그인을 시작합니다.

HTML 예시:

<a href="/oauth2/authorization/google">Google 로그인</a>

이 링크를 통해 구글 로그인 페이지로 이동하여 인증을 시작합니다.

 

5. 로그인 후 사용자 정보 처리

로그인 후 사용자의 정보를 처리하기 위해, OAuth2UserService 또는 OidcUserService를 구현하여 사용자 정보를 커스터마이징할 수 있습니다.

import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUserRequest;
import org.springframework.security.oauth2.core.oidc.user.OidcUserService;
import org.springframework.stereotype.Service;

/**
 * OpenID Connect(OAuth2.0 기반) 로 인증 후처리 (예 - Google)
 */
@Service
public class CustomOidcUserService extends OidcUserService {

    @Override
    public OidcUser loadUser(OidcUserRequest userRequest) {
        
        // ID Token 가져오기
        final String idToken = userRequest.getIdToken();
        
        // 사용자 ID 가져오기
        final String userName = idToken.getSubject();

        OidcUser oidcUser = super.loadUser(userRequest);

        // 사용자 정보 가져오기
        String email = oidcUser.getEmail();

        // 사용자 정보 커스터마이징 로직 추가 가능
        // ...

        return oidcUser;
    }
}
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        
        String platformUserId = this.getPlatformUserId(userRequest);
        
        // 사용자 정보를 DB에 저장하거나 추가 로직을 처리할 수 있습니다.
        
        return oAuth2User;
    }

    private String getPlatformUserId(OAuth2UserRequest userRequest, String registrationId) {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        Map<String, Object> attributes = oAuth2User.getAttributes();
        String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();

        if (registrationId.equals(OAuth2Provider.NAVER.lowerName())) {
            Map<String, Object> response = (Map<String, Object>) attributes.get(userNameAttributeName);
            return (String) response.get("id");
        }

        return (String) attributes.get(userNameAttributeName);
    }
}

 

 

6. 테스트 및 디버깅

  • 애플리케이션을 실행한 후 브라우저에서 로그인 페이지로 이동하여, OAuth 제공자(Google, Naver 등)를 통해 로그인이 잘 되는지 테스트합니다.
  • OAuth2 인증이 완료되면, Spring Security는 자동으로 해당 사용자의 세션을 생성하고 인증 상태를 유지합니다.

 

7. (선택) 리프레시 토큰 및 추가 기능 구현

필요하다면 OAuth2 리프레시 토큰을 사용하여 액세스 토큰을 갱신하거나, 추가적인 사용자 데이터 처리 로직을 구현할 수 있습니다.

 

구글 토큰 검증 예시:

	public boolean verifySignIn(String idToken, String googleUserId) throws RuntimeException {

		//Global instance of the JSON factory.
		final String CLIENT_ID = clientRegistrationRepository.findByRegistrationId("google").getClientId();
		final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

		// Static HTTP transport factory used by the API to communicate with the Game Services API.
		NetHttpTransport NET_HTTP_TRANSPORT = new NetHttpTransport();

		//참고: https://developers.google.com/identity/sign-in/web/backend-auth
		GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(NET_HTTP_TRANSPORT, JSON_FACTORY)
			// Specify the CLIENT_ID of the app that accesses the backend:
			.setAudience(Collections.singletonList(CLIENT_ID))
			// Or, if multiple clients access the backend: .setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
			.build();

		GoogleIdToken googleIdToken;
		try { // 공통화 처리를 위해 check exception을 unchecked exception으로 핸들링.
			googleIdToken = verifier.verify(idToken);
		} catch (Exception e) {
			return false;
		}
		if (googleIdToken == null) { //idToken검사결과 유효한 토큰이 아님
			return false;
		}

		GoogleIdToken.Payload payload = googleIdToken.getPayload();

		//동일한 userId의 토큰인지 검사
		if (!StringUtils.equals(payload.getSubject(), googleUserId)) {
			return false;
		}

		return true;
	}
728x90
반응형

댓글