들어가며
나 자신에 대해 많이 실망하게 된 한 주였다.
내가 맡은 부분을 다른 사람들은 시간 내에 해결하고 있는데, 나는 결국 일주일 안에 해결하지 못했다.
이번 한주의 WIL은 내가 맡은 '소셜로그인' 부분을 공부하면서 막혔던 부분에 대해 작성해보고자 한다.
소셜 로그인
우리 팀은 카카오, 네이버, 구글에서 제공하는 소셜 로그인 기능을 이용하고자 했고, 해당 부분을 내가 맡게 되었다.
로그인 방식 하나를 먼저 구현해두면 다른 부분은 거의 비슷하다는 말에 따라 가장 흔히 접하는 카카오 로그인부터 구현하고자 하였다.
그냥 로그인만 구현하고자 하면 다음과 같이 간단하게 구현할 수 있다.
카카오 소셜 로그인
카카오 소셜 로그인 flow
Step1. 인가 코드 받기
- 서비스 서버가 카카오 인증 서버로 인가 코드 받기를 요청한다.
- 카카오 인증 서버가 사용자에게 카카오계정 로그인을 통한 인증을 요청한다.
- 사용자가 카카오계정으로 로그인한다.
- 카카오 인증 서버가 사용자에게 동의 화면을 출력하여 인가를 위한 사용자 동의를 요청한다.
- 사용자가 필수 동의 항목, 이 외 원하는 동의 항목에 동의한 뒤, [동의하고 시작하기] 버튼을 클릭한다.
- 카카오 인증 서버는 서비스 앱의 Redirect URI로 인가 코드를 전달한다.
Step2. 토큰 받기
- 서비스 서버가 Redirect URI를 통해 전달받은 인가 코드로 토큰 받기를 요청한다.
- 카카오 인증 서버가 토큰을 발급하여 서비스 서버에 전달한다.
토큰은 엑세스 토큰(Access token), 리프레시 토큰(Refresh token), ID 토큰(ID token) 세 종류이다.
토큰은 동의 화면을 통해 사용자 동의를 받은 인가 정보를 포함한다.
ID 토큰은 OpenId Connect 사용 시에만 발급된다.
Step3. 사용자 로그인 처리
- 서비스 서버가 발급받은 엑세스 토큰으로 사요자 정보 가져오기를 요청해 사용자의 회원번호 및 정보를 조회하여 서비스 회원인지 확인한다.
- 서비스 회원 확인 결과에 따라 서비스 로그인 또는 회원 가입 과정을 진행한다.
- 이 외 서비스에서 필요한 로그인 절차를 수행한 후, 카카오 로그인한 사용자의 서비스 로그인 처리를 완료한다.
구현하기
구현하기는 그냥 카카오 로그인을 통해 엑세스 토큰을 발급받고, 정보만 뿌려주면 되는게 아니라, Spring Security와 JWT토큰도 함께 적용해야했다.
해당 부분에 관련된 여러가지 자료를 읽어보았는데, 많은 글들을 보아도 그 플로우가 이해가 되질 않았다.
참고한 글을 목록은 다음과 같다.
[Spring Boot] OAuth2 소셜 로그인 가이드 (구글, 페이스북, 네이버, 카카오)
좋은 글이지만 구현해놓은 클래스가 너무 많다. 더 간단하게 구현할 수 있지 않을까?
Spring Security + OAuth2.0 소셜 인증 예제(Google, Naver, Kakao)
이 글은 User entity와 socialAuth entity를 분리해두는게 맘에 들지 않았다. 그냥 user에 다 집어넣고 싶은데…
[Spring Security] OAuth 구글 로그인하기
jwt 토큰에 대한 내용은 들어있지 않아 따로 구현해야함.
[Springboot, JWT] JWT를 이용한 소셜 로그인 구현하기 - Kakao/Google
WebClient를 추가로 사용한 글
Spring-React : 카카오 소셜 로그인 + JWT (1) 카카오 엑세스 토큰 발급 받기
인가코드를 넘겨받는 로직을 프론트에 구현해놓은 글. jwt 미포함
정말 다양한 방식으로 소셜 로그인을 구현해두었기도 하고, 아직 이해가 부족한 Spring Security와 JWT를 공부하면서 코드를 적용하려니 이해가 잘 되지 않고, 어떻게 내 방식대로 적용해야하는지 막막했다.
따라서 최종 선택은 다음 글의 코드를 클론코딩하면서 '공부'를 하는 방식으로 진행하고자 했다.
[Spring Boot] OAuth2 소셜 로그인 가이드 (구글, 페이스북, 네이버, 카카오)
해당 글은 구현해야하는 클래스가 굉장히 많지만, 그만큼 코드들이 깔끔하고 공부하기 좋은 코드라고 생각이 들었다.
공부 & 트러블 슈팅
위 코드를 클론코딩 하면서 계속 헷갈리던 개념들을 정리해 보았다.
Jackson vs Gson
Jackson과 Gson은 JSON 데이터 바인딩 지원을 제공하는 라이브러리이다.
JPA를 쓰다가 Jackson의 직렬화 때문에 stackoverflow 에러를 마주한 적이 있었는데, 그때 그 Jackson이 맞다.
이 둘간의 차이점은 무엇이 있는 지 알아보자!
Jackson
Jackson은 Spring에서 기본으로 제공하는 라이브러리이다.
기본으로 제공해주는 라이브러리인 만큼 속도가 빠르고 유연한 장점이 있다.
또, 고용량의 JSON 데이터 처리 성능이 탁월하며, Json뿐만 아니라 xml과 yaml도 지원한다.
Gson
Gson은 구글에서 제공해주는 라이브러리로, Jackson과 달리 Json만 처리가 가능하다.
간단한 기능만을 제공하고 있기 때문에 가벼운 Json 데이터를 처리할 때 성능이 좋고, 비교적 가볍다고 한다.
인증(Authentication)과 인가(Authorization)
인증(Authentication)
유저가 누구인 지 확인하는 절차. 회원가입하고 로그인하는 것.
소셜 로그인이나 일반로그인에서 jwt 토큰을 가지고 주로 인증과정을 거침.(물론 쿠키와 세션을 사용하는 방식도 있다)
인가(Authorization)
유저에 대한 권한을 허락하는 것. Spring Security에서 주로 처리함. 역시 jwt를 이용하여 처리함.
JWT(Json Web Token)
JWT의 구조
- Header
- alg : 최종적으로 만들어지는 JWT 토큰을 검증할 때 사용되는 해싱 알고리즘
- typ: 타입
- Payload
- Registerd Claims : 토큰에 대한 정보
- 발급자
- 제목
- 대상자
- 만료 시각
- 활성화 날짜
- 발급 시각
- Public Claims : 사용자 정의 claim. 충돌을 방지하기 위해 key를 UUID나 URI로 정의해야한다.
- Private Claims : 사용자 정의 claim. 통신하고 있는 서버-클라이언트가 공유하기 위한 데이터
- Registerd Claims : 토큰에 대한 정보
- Signature : 토큰의 무결성을 판단하기 위해 사용. 인코딩된 헤더, 페이로드, 비밀키, 헤더에 정의된 서명 알고리즘을 이용하여 생성
JWT의 탈취
리프레쉬 토큰을 쿠키에 저장하거나 클라이언트에게 주지 않고 DB에만 저장하고 사용할 수는 없을까?
일반적으로 access token과 refresh token을 모두 클라이언트에게 전달하거나, refresh token은 서버의 쿠키(혹은 세션)에 저장해두고 access token을 클라이언트에게 전달하는 방식으로 사용하고 있다.
그런데, 쿠키는 아무리 안전하게 관리한다고 해도 탈취당할 위험이 있고, 세션은 사용자가 많아지면 서버에 부담이 커지는 문제가 있다. 그럼 그냥 DB에 refresh token을 집어넣어놓고 그걸 꺼내서 쓰면 안되는걸까?
일단 지금까지 찾아본 바로는 refresh token을 DB에만 저장해두고, 클라이언트에게 제공하지 않는다면 access token을 탈취한 공격자가 토큰 재발급을 요청했을 때 어떠한 검증 절차도 없이 무작정 DB에서 refresh token을 꺼내서 재발급해주게 되기 때문에 클라이언트도 refresh token을 가지고 있어야한다는게 결론이다.
그럼 클라이언트한테 전달해준 refresh token도 탈취되면 어떻게 되는거지?
이를 방지하기 위해 httpOnly 설정을 추가해준다. httpOnly 옵션을 사용하면 웹페이지의 Javascript에서 쿠키에 접근 자체가 불가능하고 한다. 따라서 XXS 공격에 상대적으로 안전하다. 물론 이 옵션으로 쿠키의 내용을 볼 수 없다고 하더라도 완전히 안전한 것은 아니다.
쿠키는 자동으로 request에 실리는 특성이 있기 때문에 공격자가 request url만 안다면 사용자가 관련 link를 클릭하도록 유도하여 request를 위조하기 쉽다.
이를 CSRF 공격이라고 한다.
이렇게 refresh token을 쿠키에 담는 이유를 정리해보았다.
더 좋은 방법이 있으면 좋겠지만… 아직은 잘 모르겠다.
회고록
변명이 가득한 이번 한주.
신체적으로, 정신적으로 엉망진창이 되는 바람에 책상 앞에 앉아 있는 시간이 길지 않았다. 그저 팀원들에게 미안할 뿐이다. 다른 팀들은 벌써 끝내고 다른 기능들 구현하고 있을텐데 나때문에 진도가 안나가는 기분이다. 결국 맡은 부분을 일주일안에 끝내지도 못했고.
많은 후회가 밀려오지만, 그럼에도 해야지...
다음 한 주도 화이팅이다.
참고 및 출처
'Study > WIL' 카테고리의 다른 글
[WIL] 항해99 week10 (07/11 ~ 07/17) (0) | 2022.07.17 |
---|---|
[WIL] 항해99 week9 (07/04 ~ 07/10) (0) | 2022.07.10 |
[WIL] 항해99 week7 (06/20 ~ 06/26) (0) | 2022.06.26 |
[WIL] 항해99 week6 (06/13 ~ 06/19) (0) | 2022.06.21 |
[WIL] 항해99 week5 (06/06 ~ 06/12) (0) | 2022.06.13 |