반응형
- [Spring Boot] OAuth 2.0 로그인 (구글 로그인)에서 구글 로그인과 회원가입에 대해 정리했었음
- 구글 뿐 아니라 카카오, 네이버, 페이스북 로그인에 대해서 정리
- 과정이 거의 비슷하긴 하지만 조금씩 다르기 때문에 한 번씩 정리해 봄
카카오 개발자 페이지에서 애플리케이션 등록
- 카카오 개발자 페이지 접속 후 로그인
- 내 어플리케이션 클릭
- 애플리케이션 추가하기
- 앱 아이콘, 앱 이름, 사업자명 작성
- 앱 키 -> REST API 키 저장
- 보안 -> Client Secret 생성 후 코드도 저장 + 활성화
- 카카오 로그인 -> 활성화 설정 둘 다 ON으로 변경 + Redirect URI 설정
- Redirect URI는 http://localhost:8080/login/oauth2/code/kakao로 지정
- 플랫폼 -> Web 플랫폼 등록 -> http://localhost:8080 설정
- 동의항목 -> 가져올 정보값 선택 (일부 항목은 검수를 해야 필수 동의 설정 가능)
- 닉네임, 이메일만 선택하고 진행해 봄
네이버 개발자 센터에서 애플리케이션 등록
- 네이버 개발자 센터 접속 후 로그인
- Application -> 애플리케이션 등록
- 애플리케이션 이름 지정 후 사용 API는 네이버 로그인 선택
- 필요한 정보 선택
- 로그인 오픈 API 서비스 환경에서 PC 웹 선택 후 URL 지정
- 서비스 URL : http://localhost:8080
- Callback URL : http://localhost:8080/login/oauth2/code/naver
- Client ID와 Client Secret을 저장
페이스북 API 콘솔에서 애플리케이션 등록
- 페이스북 개발자 센터 접속 후 로그인
- My Apps -> 앱 만들기
- 유형 : 소비자 선택
- 앱 이름, 이메일, 비지니스 계정 설정 후 앱 만들기
- 방금 생성한 앱 대시보드에 접속 후 앱에 제품 추가하기에서 Facebook 로그인 선택
- 플랫폼 : 웹 선택
- 사이트 URL에 http://localhost:8080 넣어주고 save
- 설정 -> 기본설정에서 앱 ID와 앱 시크릿 코드를 저장
각각의 사이트에서 받아온 값 정리
- 전에 했던 구글 + 카카오, 네이버, 페이스북 OAuth 로그인 시 받아오는 정보를 getAttributes를 통해 출력해 봄
- 추가 항목 선택 시 값을 더 받아올 수도 있음
<google>
{
sub=103058387739722400130,
name=안창범,
given_name=창범,
family_name=안,
picture=https://lh3.googleusercontent.com/a/AEdFTp5SiCyTaOLog9sDPN6QhWwsUj7xPbfj4HQF0fdC=s96-c, email=chb20050@gmail.com,
email_verified=true,
locale=ko
}
<kakao>
{
id=2632890179,
connected_at=2023-01-22T08:17:54Z,
properties = {nickname=안창범},
kakao_account = {
profile_nickname_needs_agreement=false,
profile={nickname=안창범},
has_email=true,
email_needs_agreement=false,
is_email_valid=true,
is_email_verified=true,
email=chb2005@naver.com
}
}
<naver>
{
resultcode=00,
message=success,
response = {
id=pvdq1FSG3VZlD7Cp3JuWfAFi-3xir6A-WPlP5f8kXIo, email=chb20050@gmail.com,
name=안창범
}
}
<facebook>
{
id=5483543425087412,
name=안창범,
email=chb2005@naver.com
}
- google과 facebook과는 달리 kakao와 naver에서 받아온 정보는 객체 안에 객체가 있는 형식이라 추출할 때, 다른 처리 방식이 필요함
- 또한 kakao의 ID는 String 타입이 아닌 Long 타입이기 때문에 추출할 때, 다른 처리 방식이 필요함
받아온 데이터 변환
- 위에서 확인한 값들을 우리가 만든 User 엔티티에 맞게끔 변환해서 가입을 시켜줘야 함
- provider = "google", "kakao", "naver", "facebook"
- providerId = google의 sub값, kakao와 facebook의 id값, naver의 response의 id 값
- loginId = provider_providerId 로 설정
- nickname = 각 사이트에 등록한 이름으로 설정
- email = google과 facebook의 email값, kakao의 kakao_account의 email값, naver의 response의 email값
코드 구현
- [Spring Boot] OAuth 2.0 로그인 (구글 로그인) 에서 코드를 추가하는 방식으로 진행
application.yml
- 카카오와 네이버는 provider도 작성해 줘야 함
- 각각의 사이트에서 발급받아 저장해 놓았던 client-id, client-secret 값을 알맞게 넣어주면 됨
spring:
# OAuth 로그인
security:
oauth2:
client:
registration:
google:
client-id: 발급받은 클라이언트 ID
client-secret: 발급받은 클라이언트 보안 비밀번호
scope:
- email
- profile
kakao:
client-id: 발급받은 REST API 키
client-secret: 발급받은 Client Secret 코드
scope:
- account_email
- profile_nickname
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/kakao
client-name: Kakao
client-authentication-method: POST
naver:
client-id: 발급받은 Client ID
client-secret: 발급받은 Client Secret
scope:
- name
- email
client-name: Naver
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/naver
facebook:
client-id: 발급받은 앱 ID
client-secret: 발급받은 앱 시크릿 코드
scope:
- email
- public_profile
provider:
kakao:
authorization-uri: https://kauth.kakao.com/oauth/authorize
token-uri: https://kauth.kakao.com/oauth/token
user-info-uri: https://kapi.kakao.com/v2/user/me
user-name-attribute: id
naver:
authorization-uri: https://nid.naver.com/oauth2.0/authorize
token-uri: https://nid.naver.com/oauth2.0/token
user-info-uri: https://openapi.naver.com/v1/nid/me
user-name-attribute: response
OAuth2UserInfo (interface)
- 사이트별로 값을 추출하는 방식은 다르지만 추출해야 하는 값은 같기 때문에 통일성을 위해 interface 생성
- 생성한 interface를 상속받아 사용
public interface OAuth2UserInfo {
String getProviderId();
String getProvider();
String getEmail();
String getName();
}
GoogleUserInfo
@AllArgsConstructor
public class GoogleUserInfo implements OAuth2UserInfo{
private Map<String, Object> attributes;
@Override
public String getProviderId() {
return (String) attributes.get("sub");
}
@Override
public String getProvider() {
return "google";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
KakaoUserInfo
@AllArgsConstructor
public class KakaoUserInfo implements OAuth2UserInfo{
private Map<String, Object> attributes;
@Override
public String getProviderId() {
// Long 타입이기 때문에 toString으로 변호나
return attributes.get("id").toString();
}
@Override
public String getProvider() {
return "kakao";
}
@Override
public String getEmail() {
// kakao_account라는 Map에서 추출
return (String) ((Map) attributes.get("kakao_account")).get("email");
}
@Override
public String getName() {
// kakao_account라는 Map에서 추출
return (String) ((Map) attributes.get("properties")).get("nickname");
}
}
NaverUserInfo
@AllArgsConstructor
public class NaverUserInfo implements OAuth2UserInfo{
private Map<String, Object> attributes;
@Override
public String getProviderId() {
return (String) attributes.get("id");
}
@Override
public String getProvider() {
return "naver";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
FacebookUserInfo
@AllArgsConstructor
public class FacebookUserInfo implements OAuth2UserInfo{
private Map<String, Object> attributes;
@Override
public String getProviderId() {
return (String) attributes.get("id");
}
@Override
public String getProvider() {
return "facebook";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
PrincipalOauth2UserService
- OAuth2UserRequest에서 provider 추출
- 추출한 provider에 따라 각각 다른 방식으로 값 추출 후 User Entity로 변환
@Service
@RequiredArgsConstructor
@Slf4j
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder encoder;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
log.info("getAttributes : {}", oAuth2User.getAttributes());
OAuth2UserInfo oAuth2UserInfo = null;
String provider = userRequest.getClientRegistration().getRegistrationId();
if(provider.equals("google")) {
log.info("구글 로그인 요청");
oAuth2UserInfo = new GoogleUserInfo( oAuth2User.getAttributes() );
} else if(provider.equals("kakao")) {
log.info("카카오 로그인 요청");
oAuth2UserInfo = new KakaoUserInfo( (Map)oAuth2User.getAttributes() );
} else if(provider.equals("naver")) {
log.info("네이버 로그인 요청");
oAuth2UserInfo = new NaverUserInfo( (Map)oAuth2User.getAttributes().get("response") );
} else if(provider.equals("facebook")) {
log.info("페이스북 로그인 요청");
oAuth2UserInfo = new FacebookUserInfo( oAuth2User.getAttributes() );
}
String providerId = oAuth2UserInfo.getProviderId();
String email = oAuth2UserInfo.getEmail();
String loginId = provider + "_" + providerId;
String nickname = oAuth2UserInfo.getName();
Optional<User> optionalUser = userRepository.findByLoginId(loginId);
User user = null;
if(optionalUser.isEmpty()) {
user = User.builder()
.loginId(loginId)
.nickname(nickname)
.provider(provider)
.providerId(providerId)
.role(UserRole.USER)
.build();
userRepository.save(user);
} else {
user = optionalUser.get();
}
return new PrincipalDetails(user, oAuth2User.getAttributes());
}
}
login.html 추가 사항
- 해당 a 태그 클릭 시 각각의 사이트 로그인 진행
<a href="/oauth2/authorization/google">구글 로그인</a>
<a href="/oauth2/authorization/kakao">카카오 로그인</a>
<a href="/oauth2/authorization/naver">네이버 로그인</a>
<a href="/oauth2/authorization/facebook">페이스북 로그인</a>
결과
- 4번 User : 직접 회원가입한 유저
- 5번 User : 구글 로그인으로 가입한 유저
- 6번 User : 카카오 로그인으로 가입한 유저
- 7번 User : 네이버 로그인으로 가입한 유저
- 8번 User : 페이스북 로그인으로 가입한 유저
반응형
'Spring Boot > 문법 정리' 카테고리의 다른 글
[Spring Boot] AWS S3를 이용한 파일 업로드 (1) | 2023.04.18 |
---|---|
[Spring Boot] Jpa와 PostgreSQL 연동 (0) | 2023.03.23 |
[Spring Boot] OAuth 2.0 로그인 (구글 로그인) (3) | 2023.01.21 |
[Spring Boot] JpaRepository를 활용한 페이징 기능 구현 + 정렬, 검색, 알림창 띄우기를 활용한 예제 (0) | 2023.01.14 |
[Spring Boot] JSON을 활용한 API 통신 예제 + JSON 형변환 (Gson, ObjectMapper, JSONParser) (0) | 2023.01.11 |