- 오늘한일
- 카카오 로그인 구현하기
소셜 로그인 탄생 배경
- 모든 웹 사이트에서 회원가입 과정을 거치는 것은 사용자에게 부담이 됩니다.
- 매번 번거로운 회원가입 과정을 수행해야 할 뿐 아니라, 웹 사이트마다 다른 아이디와 비밀번호를 기억해야 합니다.
- 웹 사이트를 운영하는 측에서도 회원들의 개인정보를 지켜야하는 역할이 부담이 됩니다.
- 바이러스와 백신의 관계 처럼, 발전하는 해킹 기술을 막기 위해 보안을 강화하는 노력이 지속적으로 필요하기 때문이죠.
- 이런 문제를 해결하기 위해 OAuth 를 사용한 소셜 로그인이 등장합니다.
OAuth탄생 배경
- "A" 서비스에서 카카오의 정보(리소스)를 가져오기 위해서는 카카오의 ID PW를 직접 입력 받아서
- "A" 서비스에 저장해서 필요할때마다 불러와서 사용을 해야했는데.
이렇게 사용하면
1. 사용자는 'A' 서비스에 카카오의 ID와 PW를 넘겨주는 것에 대해 신뢰할 수 없다.
2. 'A' 서비스는 카카오의 ID와 PW를 저장하기 때문에 보안 문제가 생기는 경우 모든 책임을 져야한다.
3. 카카오는 'A' 어플리케이션을 신뢰할 수 없다
라는 문제가 생깁니다.
이문제를 해결하기 위해 OAuth를 도입하여 인증을 외부 어플리케이션에 위임하여 처리하도록 해결합니다.
OAuth 란?
- OAuth의 사전적 정의는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹 사이트 상의 자신들의 정보에 대해 웹 사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는 접근 위임을 위한 개방형 표준이다.
- OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준입니다. 사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수 있게 해주는 HTTP 기반의 보안 프로토콜 입니다. OAuth를 사용하는 서비스 제공자는 대표적으로 구글, 페이스북 등이 있습니다. 국내에는 대표적으로 네이버와 카카오가 있습니다.
OAuth 2.0 용어 (구성 요소)
ex)
애플,페이스북,구글,카카오,네이버,트위터 등 제공한다.
카카오 로그인 사용 승인 받기
1. 내 애플리케이션 메뉴 선택 > 애플리케이션 추가하기
2. 앱 아이콘, 앱 이름, 사업자명 저장
3. 사이트 도메인 등록하기
- 애플리케이션 선택
- 플랫폼 메뉴 선택 > 플랫폼 설정하기 클릭
- Web 플랫폼 등록
- 사이트 도메인 입력
4. 카카오로 로그인 했을 때 인가토큰을 받게 될 Redirect URI (callback) 를 설정하기
5. 동의항목 설정하기
카카오 사용자 정보 가져오기
카카오 인가코드 받기
https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code
카카오에서 보내주는 '인가코드' 처리
UserController
@GetMapping("/user/kakao/callback")
public String kakaoLogin(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException {
// code: 카카오 서버로부터 받은 인가 코드 Service 전달 후 인증 처리 및 JWT 반환
String token = kakaoService.kakaoLogin(code);
// Cookie 생성 및 직접 브라우저에 Set
Cookie cookie = new Cookie(JwtUtil.AUTHORIZATION_HEADER, token.substring(7));
cookie.setPath("/");
response.addCookie(cookie);
return "redirect:/";
}
- 카카오에서 보내주는 '인가코드'를 받음 ⇒ Controller
- '인가코드'를 가지고 카카오 로그인 처리 ⇒ Service
- 로그인 성공 시 "/" 으로 redirect ⇒ Controller
카카오 사용자 정보 가져오기
1."인가 코드"로 "액세스 토큰" 요청
private String getToken(String code) throws JsonProcessingException {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com")
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "본인의 REST API키");
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code);
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(body);
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
}
2."액세스 토큰"으로 "카카오 사용자 정보" 가져오기
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kapi.kakao.com")
.path("/v2/user/me")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(new LinkedMultiValueMap<>());
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
String email = jsonNode.get("kakao_account")
.get("email").asText();
log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
return new KakaoUserInfoDto(id, nickname, email);
}
카카오 사용자 정보로 회원가입 구현
카카오 사용자 회원가입 설계
- 카카오로 부터 받은 사용자 정보
- kakaoId
- nickname
테이블 설계 옵션
- 카카오 User 를 위한 테이블 (ex. KakaoUser) 을 하나 더 만든다.
- 장점: 결합도가 낮아짐
- 성격이 다른 유저 별로 분리 → 차후 각 테이블의 변화에 서로 영향을 주지 않음
- 예) 카카오 사용자들만 profile_image 컬럼 추가해서 사용 가능
- 단점: 구현 난이도가 올라감
- 예) 관심상품 등록 시, 회원별로 다른 테이블을 참조해야 함
- 일반 회원: User - Product
- 카카오 회원: KakaoUser - Product
- 예) 관심상품 등록 시, 회원별로 다른 테이블을 참조해야 함
- 장점: 결합도가 낮아짐
- 기존 회원 (User) 테이블에 카카오 User 추가
- 장점: 구현이 단순해짐
- 단점: 결합도가 높아짐
회원 (User) 테이블에 적용시킨 예)
- 패스워드를 UUID 로 설정한 이유: 폼 로그인을 통해서 로그인되지 않기위해
- 카카오 사용자 정보로 회원가입
- User 테이블에 'kakaoId' 추가
- 회원 가입 처리
-DB에 kakaoId 를 가진 회원이 없을 경우에만 신규로 회원 가입을 진행합니다.
public String kakaoLogin(String code, HttpServletResponse response) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
// 3. 필요시에 회원가입
User kakaoUser = registerKakaoUserIfNeeded(kakaoUserInfo);
// 4. JWT 토큰 반환
String createToken = jwtUtil.createToken(kakaoUser.getUsername(), kakaoUser.getRole());
return createToken;
}
private User registerKakaoUserIfNeeded(KakaoUserInfoDto kakaoUserInfo) {
// DB 에 중복된 Kakao Id 가 있는지 확인
Long kakaoId = kakaoUserInfo.getId();
User kakaoUser = userRepository.findByKakaoId(kakaoId).orElse(null);
if (kakaoUser == null) {
// 카카오 사용자 email 동일한 email 가진 회원이 있는지 확인
String kakaoEmail = kakaoUserInfo.getEmail();
User sameEmailUser = userRepository.findByEmail(kakaoEmail).orElse(null);
if (sameEmailUser != null) {
kakaoUser = sameEmailUser;
// 기존 회원정보에 카카오 Id 추가
kakaoUser = kakaoUser.kakaoIdUpdate(kakaoId);
} else {
// 신규 회원가입
// password: random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
// email: kakao email
String email = kakaoUserInfo.getEmail();
kakaoUser = new User(kakaoUserInfo.getNickname(), encodedPassword, email, UserRoleEnum.USER, kakaoId);
}
userRepository.save(kakaoUser);
}
return kakaoUser;
}
느낀점
- OAuth에 대해 알게 되었고 카카오 소셜 로그인 회원가입 적용법에 대해 알게 되었다,
'TIL' 카테고리의 다른 글
231130_TIL (0) | 2023.12.01 |
---|---|
231129_TIL (0) | 2023.11.29 |
231117_TIL (1) | 2023.11.17 |
231110_TIL (0) | 2023.11.10 |
231109_TIL (1) | 2023.11.09 |