[Spring Boot] (5) 로그인/회원가입 - Oauth2를 이용한 카카오 로그인 :: 음악학원 홈페이지 프로젝트

2020. 9. 1. 23:48Spring Boot

https://codecrafting.tistory.com/10 에서 이어집니다.

 

[Spring Boot] (4) 로그인/회원가입 - 자사 로그인 :: 음악학원 홈페이지 프로젝트

https://codecrafting.tistory.com/9 에서 이어지는 포스팅입니다. [Spring Boot] (2) 로그인/회원가입 - Spring Security 설정 :: 음악학원 홈페이지 프로젝트 들어가기 전에 ... 스프링 시큐리티 Spring Securi..

codecrafting.tistory.com

 

지난 시간에는 자사 로그인 서비스를 설명하면서 개발자의 서비스 클래스를 이용하려면 UserService 인터페이스를 구현한 클래스를 AuthenticationManagerBuilder의 userDetailService의 인자로 걸어주면 된다고 했었습니다.

소셜 로그인의 경우는 조금 다른데, Security Config에 서비스 클래스를 명시해주는 것은 똑같지만 UserService 인터페이스를 구현하는 것이 아닌, OAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> 인터페이스를 구현한 클래스를 걸어주어야 합니다.

 

 

 

이렇게 말이죠!

이제 CustomOAuth2UserService 안의 내용을 보겠습니다. 

 

1. src/main/java/com/soriel/music/springboot/service/soriel/CustomOAuth2UserService.java

registrationId

현재 로그인 진행중인 서비스 코드를 구분하기 위한 코드입니다. 예) 네이버 로그인인지, 구글 로그인인지 구별

 

userNameAttributeName

OAuth2 로그인 진행 시 키가 되는 필드 값을 이야기합니다. PK와 같은 의미입니다. 구글의 경우 기본적으로 코드를 제공하지만 네이버나 카카오 등은 지원하지 않는다고 하네요.

 

OAuthAttributes

OAuth2UserService를 통해 가져온 OAuth2User의 attribute를 담을 클래스입니다. 저자는 해당 클래스를 dto로 간주하고 코드를 작성하였습니다.

 

이제 OAuthAttributes 클래스를 생성합니다. 저자는 해당 클래스를 DTO로 간주하고 있었습니다.

 

 

2. src/main/java/com/soriel/music/springboot/web/dto/member/OAuthAttributes.java

 

of()

OAuth2User에서 반환하는 사용자 정보가 Map으로 되어있기 때문에 값을 꺼내주기 위해 필요한 메서드입니다. of는 ofKakao 메소드를 호출하는데 여기에서 attributes안에 들어있는 인자들을 적절히 조합해서 필드에 값을 채운 후 스스로를 리턴하게 됩니다.

 

attributes의 내부는 중첩 객체들로 쌓여있기 때문에 kakao_account와 profile을 따로따로 분리시켜서 각각의 Map에 담습니다. 그리고 필요한 데이터(이메일, 이름, 고유id값)을 빼내어 빌더에 담으면 됩니다.

 

Map 형태의 attributes 내부는 이렇게 되어있습니다.

더보기


 
 id = 고유 id값,

 connected_at=2020-07-14T14:52:25Z,

 properties={
    
    nickname=이름,
    
    profile_image=프로필 이미지 주소,
    
    thumbnail_image=썸네일 이미지 주소,
    
    kakao_account={

       profile_needs_agreement=false,

       profile={
       
          nickname=이름
       
       },

       has_email=true,

       email_needs_agreement=false,

       is_email_valid=true,

       is_email_verified=true,

       email=이메일
    
    }

}

 

toEntity()

변환한 사용자 정보를 이용해 IntegrationEntity 엔티티를 생성합니다. IntegrationEntity 엔티티를 생성하는 시점은 CustomOAuth2UserService에서 작성한 것처럼 처음 가입할 때이며 가입 시 기본 권한을 MEMBER로 주기 위해 role 빌더값에는 Role.MEMBER 를 사용합니다.

 

 

다시 CustomOAuth2UserService로 돌아와봅시다.

 

 

saveOrUpdate(attributes)

of 메소드를 통해 OAuthAttributes 객체를 리턴받은 후, static method saveOrUpdate메소드에 OAuthAttributes 객체를 인자로 담아 이미 integrationRepository에 계정이 들어있다면 해당 계정을 업데이트하고 저장합니다. 계정이 없으면 새로운 엔티티 객체를 리턴후에 저장합니다.

 

모든 과정이 끝나면  IntegrationEntity 엔티티 객체가 리턴됩니다.

 

3. src/main/java/com/soriel/music/springboot/domain/member/IntegrationEntity.java

@Getter
@Entity(name = "member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class IntegrationEntity extends BaseTimeEntity {
    //가제

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 50, unique = true, nullable = false)
    private String email;

    @Column(nullable = false, unique = true)
    private String name;

    @Column(nullable = true, length = 200)
    private String upwd;

    @Enumerated(EnumType.STRING)
    @Column
    private Role role;

    @Builder
    public IntegrationEntity(Long id, String email, String name, String upwd, Role role) {
        this.id = id;
        this.email = email;
        this.name = name;
        this.upwd = upwd;
        this.role = role;
    }

    public String getRoleKey() {
        return this.role.getKey();
    }

    public IntegrationEntity update(String name, String email) {
        this.name = name;
        this.email = email;

        return this;
    }
}

 

return new CustomIntegrationDto

이제 Spring Security Filter에 사용될 CustomIntegrationDto에 리턴된 IntegrationEntity 객체와, Authorities, attributes, nameAttributeKey를 담아서 리턴합니다. 

 

 

이제 마지막 단계가 남았습니다. 바로 카카오 로그인 api를 추가하는 것인데요.

먼저 카카오 로그인 API 로 이동한 후, 어플리케이션을 등록합니다. 

 

모두 등록하고 나면 사이트의 메인 페이지를 적어줍니다. 이제 카카오 로그인이 성공적으로 이루어지고 나면 여기에서 등록한 페이지로 이동하게 됩니다.

 

리다이렉트 URL은 로그인을 실행할 url 입니다. 여기에서는 ~/login/oauth2/code/kakao로 등록합니다.

이제 '요약정보' 탭에 보면 네이티브, REST API, Javascript, Admin 키가 발급되어 있습니다. 이 중 REST API 키 값을 복사해서 사용합니다.

 

 

해당 키 값들은 application-oauth.properties에 등록합니다. 카카오에서는 스프링 시큐리티를 공식 지원해주지 않기 때문에 Common Oauth2Provider을 사용할 수 없고, 직접 수동으로 입력해야 합니다.

 

4. src/main/resources/application-oauth.properties

# registration
spring.security.oauth2.client.registration.kakao.client-id=REST API 키
spring.security.oauth2.client.registration.kakao.client-name=kakao
spring.security.oauth2.client.registration.kakao.redirect-uri={baseUrl}/{action}/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.kakao.client-authentication-method=post
spring.security.oauth2.client.registration.kakao.authorization-grant-type=authorization_code

#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=kakao_account

 

 

application-oauth.properties의 내용들은 oauth2 의 인증절차 방식에 따른 url을 포함하고 있습니다.

 

4-1 인증 코드 요청

먼저 인증코드를 카카오 서버 측에서 받을 때의 리퀘스트 URL입니다 (공식 문서 참조)

GET /oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code HTTP/1.1
Host: kauth.kakao.com

REST_API_KEY와 REDIRECT_URL 는 위에서 registration 쪽에서 정의해주고 있습니다. 따라서 

spring.security.oauth2.client.provider.kakao.authorization-uri=kauth.kakao.com/oauth/authorize

으로 요청을 보내면 인증코드를 요청하고, 인증코드를 전달받을 수 있습니다.

 

 

4-2 인증코드로 토큰요청

사용자 토큰을 받는 것도 마찬가지입니다. 토큰 요청 리퀘스트 URL은 다음과 같습니다.

POST /oauth/token HTTP/1.1
Host: kauth.kakao.com
Content-type: application/x-www-form-urlencoded;charset=utf-8

따라서

spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token

으로 요청하면 됩니다.

 

4-3 토큰으로 사용자 정보요청

이제 받은 인증토큰을 사용해서 사용자 정보를 가져와봅시다. (공식 문서 참조)

GET/POST /v2/user/me HTTP/1.1
Host: kapi.kakao.com
Authorization: Bearer {USER_ACCESS_TOKEN}
Content-type: application/x-www-form-urlencoded;charset=utf-8

토큰을 받지 않고 해당 URL로 접속하면 권한오류가 발생합니다.

spring.security.oauth2.client.provider.kakao.user-info-uri=https://kapi.kakao.com/v2/user/me

 

성공적으로 데이터를 반환하면 다음과 같은 데이터 뭉치를 받게 됩니다.

더보기

HTTP/1.1 200 OK { "id":123456789, "properties":{ "nickname":"홍길동카톡", "thumbnail_image":"http://xxx.kakao.co.kr/.../aaa.jpg", "profile_image":"http://xxx.kakao.co.kr/.../bbb.jpg", "custom_field1":"23", "custom_field2":"여" ... }, "kakao_account": { "profile_needs_agreement": false, "profile": { "nickname": "홍길동", "thumbnail_image_url": "http://yyy.kakao.com/.../img_110x110.jpg", "profile_image_url": "http://yyy.kakao.com/dn/.../img_640x640.jpg" }, "email_needs_agreement":false, "is_email_valid": true, "is_email_verified": true, "email": "xxxxxxx@xxxxx.com" "age_range_needs_agreement":false, "age_range":"20~29", "birthday_needs_agreement":false, "birthday":"1130", "gender_needs_agreement":false, "gender":"female" } }

 

이때, 원하는 데이터 객체의 key를 user-name-attribute에 입력하면 해당 json 객체만 받아올 수 있습니다.

spring.security.oauth2.client.provider.kakao.user-name-attribute=kakao_account

 

이제 모든 설정이 완료되었습니다.

카카오 로그인 버튼을 누르면 이제 카카오 회원 인증을 거쳐 우리 홈페이지의 회원으로 등록이 됩니다.

 

 

스프링 시큐리티는 무턱대고 따라하기 힘든 감이 있으므로 꼭 공식문서나 참고자료를 확인하시고 따라하면 금방 배울 수 있습니다.

다음 포스팅에서는 게시판 CRUD를 다뤄보도록 하겠습니다.