oauth 세팅 중 yml과 properties가 이해되지 않을 때, 스프링 시큐리티와 프로바이더
728x90
반응형

요즘 Oauth를 활용한 회원가입 및 로그인 개발을 하는 중이다. 쉽게 얘기하면 우리가 쉽게 접하는 구글, 페이스북 등의 서비스를 통해 다른 플랫폼에서 회원가입 및 로그인을 하는 기능이다. 요즘 흔히 보이는 SNS 로그인이 바로 이것이다. 그런데 이 부분을 학습하면서 헷갈리는 부분이 매우 많다. 토큰 개념부터 리다이렉트 이슈까지. 나를 헷갈리게 한 것 중 하나를 여기서 다뤄보겠다. 바로 Provider이다.

 

혹시 Oauth에 대해 개념 정리가 잘 안되는 분들에게는 위 링크와 별도로 아래 링크도 참고하길 바란다.

 

OAuth2 서비스 인증  |  Android 개발자  |  Android Developers

온라인 서비스에 안전하게 액세스하려면 사용자는 신원을 증명하여 서비스에 인증해야 합니다. 타사 서비스에 액세스하는 애플리케이션에서 보안 문제는 훨씬 더 복잡합니다. …

developer.android.com

 

application.properties? application.yml?

스프링을 다뤄본 경험이 있다면 이 파일이 익숙할 것이다. 이것은 애플리케이션 설정 파일로 둘 모두 동일한 목적으로 사용된다. 파라미터 설정만으로 서비스 환경을 구분하여 빌드하거나 서비스를 실행시킬 수 있다. oauth를 다룬다면 이 파일에 다음과 같은 코드를 볼 수 있다. (아래 코드는 iseunghan님의 블로그를 참고했다.)

###### application-oauth.properties ######

# GOOGLE
spring.security.oauth2.client.registration.google.client-id = [클라이언트 id]
spring.security.oauth2.client.registration.google.client-secret = [클라이언트 pw]
spring.security.oauth2.client.registration.google.scope = profile, email

# 구글이나 페이스북은 안적어도 되는데, 네이버나 카카오는 적어줘야함(기본 제공 provider가 아니기 때문에)
spring.security.oauth2.client.registration.naver.client-id = [클라이언트 id]
spring.security.oauth2.client.registration.naver.client-secret= [클라이언트 pw]
spring.security.oauth2.client.registration.naver.client-name=Naver
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.redirect-uri=http://localhost:8080/login/oauth2/code/naver

# Naver Provider 등록!
spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize
spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me
spring.security.oauth2.client.provider.naver.user-name-attribute=response # 네이버가 회원정보를 json으로 넘겨주는데, response라는 키값으로 리턴해준다.

# KAKAO
spring.security.oauth2.client.registration.kakao.client-id = [클라이언트 id]
spring.security.oauth2.client.registration.kakao.client-secret = [클라이언트 pw]
spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost:8080/login/oauth2/code/kakao
spring.security.oauth2.client.registration.kakao.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.kakao.scope=profile,account_email
spring.security.oauth2.client.registration.kakao.client-name=kakao
spring.security.oauth2.client.registration.kakao.client-authentication-method=POST

## kAKAO Provider 등록!
spring.security.oauth2.client.provider.kakao.authorization-uri= https://kauth.kakao.com/oauth/authorize
spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token
spring.security.oauth2.client.provider.kakao.user-info-uri=https://kapi.kakao.com/v2/user/me
spring.security.oauth2.client.provider.kakao.user-name-attribute=id # 카카오가 회원정보를 json으로 넘겨주는데, id라는 키값으로 리턴해준다.

 

이 코드를 본 뒤, 나의 생각

 

어딘가에서 이것들을 끌어다 쓸텐데... (내 프로젝트에서 찾아본 후) 어라 없네?

 

일단 위 oauth properties 파일을 기본 properties에서 끌어다 쓴다는 것은 확인했다. 즉 어떨 때는 쓰고 어떨 때는 안쓰는 상황은 아니라는 것이다. 항상 이 설정을 사용하기 위해 기본 설정파일에도 포함시킨 것일테니.

###### application.properties ######

spring.profiles.include=oauth

# 아래 코드는 생략

 

이 궁금함을 해결하기 어려웠던 이유 중 하나는 대부분 서적 "스프링 부트와 AWS로 혼자 구현하는 웹 서비스"과 관련된 자료만 볼 수 있었기 때문이었다. properties나 yml 파일을 어떻게 사용하는지는 나오는데 정확히 어떤 역할이고 어떤 원리로 동작하는지 설명한 것을 찾기는 매우 힘들었다. 그렇게 자료를 찾다가 발견한 문구.

Spring security에서 제공하는 oauth2-client 라이브러리에는 유명한 google, facebook, twitter 등 웹사이트에 대한 Provider들은 제공을 해주지만, 우리나라에서만 한정적으로 사용하는 네이버나, 카카오 같은 서비스에 대한 정보들을 제공해주지 못한다.

 

그리고 이 문장에서 Spring security와 Provider에 집중해보았다.

 

 

 

Spring security

스프링 시큐리티는 스프링 기반의 애플리케이션의 보안을 담당하는 프레임워크이다. 여기서 보안은 인증과 권한을 포함한 개념이다. 이 인증과 권한을 필터를 거처서 처리를 한다. 벌써 version 5가 출시가 될 정도로 많이 발전했다. 발전 과정은 다른 자료에서 찾아보고 당장 뒤에 다룰 내용과 관련있는 사항만 좀 더 설명해본다.

 

먼저 인증 과정을 살펴보자. 아래 설명한 글은 대충 요약한 것이다. 중요한 것은 이 과정이 아니기 때문. 게다가 반전이 있으니 대충 요약한만큼 대충 읽자.

 

 

  1. 유저가 로그인을 요청하고 (http request)
  2. UsernamePasswordAuthenticationToken : 아이디와 비밀번호를 사용하는 form 기반 인증으로 생성됨
    (Authentication 인터페이스의 구현체)
  3. AuthenticationManager : 인증을 처리하는 방법을 정의한 API
    (ProviderManager : 위의 구현체, AuthenticationProvider 목록을 위임 받는다.)
  4. 등록할 AuthenticationProvider를 찾는다
    (AuthenticationProvider : 인증 전의 Authentication 객체를 받아서 인증된 Authentication 객체를 반환)
  5. UserDetailsService : UserDetails 객체를 반환하기 위해 유저 정보를 조회
  6. UserDetails : 유저 정보를 담기 위한 추상화된 객체
  7. 조회된 유저 정보를 반환
  8. AuthenticationManager에 AuthenticationProvider 등록
  9. AuthenticationFilter들을 순서대로 동작
  10. SecurityContextHolder : 보안 주체의 세부 정보를 포함하여 응용프래그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장되어 있다

 

일단 정리를 해 보았는데도 완전히 이해가 잘 가지 않는다. 일단 수박 겉핥기는 했으니 더 상세한 사항이 알고 싶다면 별도로 조사를 하길 권한다. 여기까지 공부한 다음 계속 필자가 적용한 Oauth와의 관계를 확인하려고 했다. 그런데 뭔가 속이 시원한 해답을 찾기 어려웠다. 당연하다. 필자가 사용할 것들은 위의 요소들이 아니기 때문. 기본 동작 매커니즘은 비슷하지만 사용하는 라이브러리가 다르다. 위 동작은 spring-security-core 라이브러리를 사용하고 필자는 spring-security-oauth2-client 라이브러리를 사용할 것이기 때문이다. 여기서는 스프링 시큐리티가 어떤 목적으로 생겨났고 인증과 관련된 개념들을 읽고 넘어가면 된다. 자 진짜 궁금한 곳을 긁으러 가보자.

 

 

CommonOAuth2Provider

"스프링 부트와 AWS로 혼자 구현하는 웹 서비스"를 기반으로 작성된 자료를 보면 구글이나 페이스북은 CommonOAuth2Provider를 통해 기본 설정값이 존재하지만 카카오나 네이버는 그렇지 않다고 나온다. 이 CommonOAuth2Provider는 spring-security-config 라이브러리에 있다.

public enum CommonOAuth2Provider {

	GOOGLE {
		@Override
		public Builder getBuilder(String registrationId) {
			ClientRegistration.Builder builder = getBuilder(registrationId,
					ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
			builder.scope("openid", "profile", "email");
			builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
			builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
			builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs");
			builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo");
			builder.userNameAttributeName(IdTokenClaimNames.SUB);
			builder.clientName("Google");
			return builder;
		}
	},

// 이하 생략

 

이처럼 enum 클래스로 존재하며 구글을 포함한 4개의 Oauth 설정값이 존재한다. 고작 4개뿐이라 수많은 Oauth 세팅이 가능한 서비스에 대응하려면 이에 맞는 프로바이더가 필요하다. 자... 다시 위에서 본 properties 파일을 보자.

 

###### application-oauth.properties ######

# GOOGLE
spring.security.oauth2.client.registration.google.client-id = [클라이언트 id]
spring.security.oauth2.client.registration.google.client-secret = [클라이언트 pw]
spring.security.oauth2.client.registration.google.scope = profile, email

# 구글이나 페이스북은 안적어도 되는데, 네이버나 카카오는 적어줘야함(기본 제공 provider가 아니기 때문에)
spring.security.oauth2.client.registration.naver.client-id = [클라이언트 id]
spring.security.oauth2.client.registration.naver.client-secret= [클라이언트 pw]
spring.security.oauth2.client.registration.naver.client-name=Naver
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.redirect-uri=http://localhost:8080/login/oauth2/code/naver

# Naver Provider 등록!
spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize
spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me
spring.security.oauth2.client.provider.naver.user-name-attribute=response # 네이버가 회원정보를 json으로 넘겨주는데, response라는 키값으로 리턴해준다.

# KAKAO
spring.security.oauth2.client.registration.kakao.client-id = [클라이언트 id]
spring.security.oauth2.client.registration.kakao.client-secret = [클라이언트 pw]
spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost:8080/login/oauth2/code/kakao
spring.security.oauth2.client.registration.kakao.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.kakao.scope=profile,account_email
spring.security.oauth2.client.registration.kakao.client-name=kakao
spring.security.oauth2.client.registration.kakao.client-authentication-method=POST

## KAKAO Provider 등록!
spring.security.oauth2.client.provider.kakao.authorization-uri= https://kauth.kakao.com/oauth/authorize
spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token
spring.security.oauth2.client.provider.kakao.user-info-uri=https://kapi.kakao.com/v2/user/me
spring.security.oauth2.client.provider.kakao.user-name-attribute=id # 카카오가 회원정보를 json으로 넘겨주는데, id라는 키값으로 리턴해준다.

구글은 registration만 존재하지만 네이버와 카카오는 registration과 provider가 페어로 존재한다. 이것이 위에서 언급한 CommonOAuth2Provider가 구글, 페이스북 등만 프로바이더를 제공해주기 때문이다. 그럼 properties나 yml에 작성된 registration과 provider은 어떻게 인식하고 적용되는 것일까.

 

OAuth2ClientProperties에 답이 있다. 재밌게도 이 클래스는 spring-boot-autoconfigure 라이브러리에 있다. 라이브러리 타이틀만 읽어도 감이 잡힐 것이다. 자동으로 설정을 잡아주기 위한 라이브러리로 Oauth에 필요한 설정값을 읽어오는 이 방법도 여기를 통해 얻어올 수 있었던 것이다.

 

이것으로 설정값을 어디서 어떻게 얻어오는지는 알았다. 그렇다면 이렇게 세팅된 값을 어떻게 쓰는 것일까? 다음 포스팅에 이어서 작성해보도록 하겠다.

 

참고자료 출처
- Spring Security OAuth2 Login Flow
- Spring Security OAuth2 Client

 

728x90
반응형