1. kakao developers - 로그인 - 내 어플리케이션
- 어플리케이션 추가
2. 어플리케이션 접속
- 환경에 맞게 key 사용 ( Rest API Key 사용할 것.. 클라이언트 키 역할)
3. 접속한 어플리케이션 - 플랫폼 설정하기
- web 플랫폼 등록
└ 도메인을 따로 구매하지 않았고 테스트 용도인 경우 : http://localhost:8000
4. 접속한 어플리케이션 - 카카오 로그인(왼쪽메뉴)
- 활성화 설정 : 상태 ON
- Redirect URI ( 로그인이 정상적으로 되고 카카오로부터 응답 받을 주소) : http://localhost:8000/auth/kakao/callback
- Logout Redirect URI : http://localhost:8000/auth/kakao/logout
5. 접속한 어플리케이션 - 동의항목 - 개인정보
- 프로필 정보(닉네임/프로필 사진) / profile
└ 동의 단계 : 필수 동의
└ 동의목적 : 카카오 로그인 개발 테스트
- 카카오계정(이메일) / account_email
└ 동의 단계 : 필수 동의(개발 과정이라 선택 불가), '선택 동의'로 일단 한다.
└ 동의목적 : 카카오 로그인 개발 테스트
6. 접속한 어플리케이션 - 간편가입
건드릴 거 없음
7. 문서 - 카카오 로그인 - 디자인 가이드
- 카카오 로그인 버튼 리소스 다운해서 resource/image/ 에 넣기 : 축약형 Middel의 png 파일 다운해서 사용
8. 문서 - REST API - 인가 코드 받기
참고 - REST API | Kakao Developers 문서1
- 요청 주소 만들고 다운 받은 버튼 이용해서 추가하기
https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code
- ${REST_API_KEY} 는 kakao developers에 등록한 어플리케이션에서 발급받은 어플리케이션 Rest API Key
- ${REDIRECT_URI} 는 kakao developers에 등록한 어플리케이션에서 설정한 Redirect URI
- response_type 는 code 고정
- 위 3개는 필수 파라미터고 추가적으로 scope, prompt 등이 있다.
html 코드
<!-- 카카오 로그인 버튼
- resource/image/ 에 버튼 이미지 추가 필요
-->
<a href="https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code"><img height="38px" src="/image/kakao_login_medium_button.png"/></a>
9. redirect 받는 controller 추가
@GetMapping("/auth/kakao/callback")
public @ResponseBody String kakaoCallback(@RequestParam String code) { // @ResponseBody를 붙이면 Data를 리턴해주는 컨트롤러함수가 된다.
System.out.println("code = " + code);
return "카카오 인증 완료";
}
10. 토큰 받기
post방식 https://kauth.kakao.com/oauth/token
application/x-www-form-urlencoded;charset=utf-8
은 key-value로 전달하라는 뜻
/*
* 카카오 서버로 요청 (POST 방식으로 key=value 데이터 형식)
* ( java POST요청은 보통 Retorfit2, OkHttp, RestTemplate 방식들 중 한개를 사용함)
* */
// HttpHeader 오브젝트 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8\n");
// HttpBody 오브젝트 생성
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code"); // authorization_code로 고정
params.add("client_id",REST_API_KEY); // 앱 REST API 키
params.add("redirect_uri", REDIRECT_URI); // 인가 코드가 리다이렉트된 URI
params.add("code", code); // 인가 코드 받기 요청으로 얻은 인가 코드
// HttpHeader와 HttpBody를 하나의 오브젝트에 담기 ( 이유 : exchange에서 HttpEntity<?>를 받기 때문)
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(params, headers);
// Http 요청 후 response에 응답받음
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
반환 받은 response에서 expires_in동안 access_token을 이용하면 scope에 지정된 범위 하에 카카오자원서버에 접근할 수 있다.
11. 사용자 정보 가져오기
// HttpHeader 오브젝트 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
headers.add("Authorization", "Bearer " +KakaoTokenResponse.getAccess_token());
// HttpBody 오브젝트 생성
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
// property_keys 포함시 400error(파싱 문제인듯?) 반환됨.
//params.add("property_keys", new String[]{"kakao_account.profile", "kakao_account.name","kakao_account.email"});
// HttpHeader와 HttpBody를 하나의 오브젝트에 담기 ( 이유 : exchange에서 HttpEntity<?>를 받기 때문)
HttpEntity<MultiValueMap<String, Object>> kakaoUserInfoRequest =
new HttpEntity<>(params, headers);
// Http 요청 후 response에 응답 받음
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoUserInfoRequest,
String.class
);
12. 카카오 정보로 로그인시 흐름
1) 카카오 로그인 버튼 클릭
2) 엑세스 토큰 요청
3) 카카오 사용자 정보 요청(엑세스 토큰을 이용)
4) 카카오 사용자 정보로 가입된 정보가 DB에 존재하는지 확인 ( username으로 확인)
5) DB에 존재하지 않으면 가입
6) 카카오 사용자 정보로 로그인 처리
- 스프링 시큐리티 로그인 처리를 위해 password를 고정 패스워드로 처리 ( 의문점! 실제 운영에서는 이렇게 하면 안될 거 같은데.. oAuth 라이브러리를 사용하면 다른가?)
- 카카오 사용자 정보에서 추가적으로 받아야 하는 정보가 있는 경우 정보 기입 화면을 띄우는 방법이 있다.
@GetMapping("/auth/kakao/callback")
public String kakaoCallback(@RequestParam String code
, @Value("${kakao.springboot_blog_project.REST_API_KEY}") String REST_API_KEY
, @Value("${kakao.springboot_blog_project.REDIRECT_URI}") String REDIRECT_URI
,@Value("${kakao.springboot_blog_project.PUBLIC_PASSWORD}") String PUBLIC_PASSWORD
) {
// TODO: 아래 과정을 service단으로 옮길 필요는 없는 건지? (@Transactional 처리)
// 카카오 토큰 요청
KakaoToken KakaoToken = getKakaoTokenRequest(code, REST_API_KEY, REDIRECT_URI);
// 카카오 사용자 정보 요청
KakaoUserInfoResponse kakaoUserInfo = getKakaoUserInfoRequest(KakaoToken);
User user = User.builder()
.username(kakaoUserInfo.getKakao_account().getEmail() + "_" + kakaoUserInfo.getId())
.password(PUBLIC_PASSWORD)
.email(kakaoUserInfo.getKakao_account().getEmail())
.oauth("kakao")
.build();
// 가입자 인지 확인
User findUser = userService.getUser(user.getUsername());
// 기존 회원이 아니면 회원 가입
if( null == findUser) {
userService.joinUser(user);
}
// 로그인 처리
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), PUBLIC_PASSWORD));
SecurityContextHolder.getContext().setAuthentication(authentication);
// 홈으로 리다이렉트
return "redirect:/";
}
참고
(1) OAuth2.0 강좌 (개념 이해하기) - YouTube
(1) oauth 2.0 (구글 API) - 2 : 동작 메커니즘 - YouTube
다른 방식으로 oAauth 구현 참고 - 스프링 시큐리티와 OAuth 2.0으로 로그인 기능 구현 (velog.io)
'개발환경, 도구 > 오픈API' 카테고리의 다른 글
springboot OAuth2 Client - facebook, naver login (0) | 2023.01.11 |
---|---|
Google API Console - OAuth login (0) | 2023.01.10 |