심화반 - 2주차 - 3
2022년 4월 16일(토)
[스파르타코딩클럽] Spring 심화반 - 2주차 - 3
◎ 소셜 로그인
- OAuth : 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준
- 사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수 있게 해주는 HTTP 기반의 보안 프로토콜
ex) 카카오, 네이버, 구글, 페이스북...
- 사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수 있게 해주는 HTTP 기반의 보안 프로토콜
◎ 카카오 로그인 사용 승인받기 (사이트)
- 카카오 로그인 사용 승인 받기 기본 구조
- 내 어플리케이션 > 어플리케이션 추가하기
- 사이트 도메인 등록하기: 어플리케이션 선택 > 플렛폼 메뉴 선택 > Web 플랫폼 등록 > 사이트 도메인 입력 (http://localhost:8080)
- 카카오로 로그인 했을 때 인가토큰을 받게 될 Redirect URI (callback) 를 설정
- 동의 항목 설정 : 프로필 정보, 카카오 계정(이메일) 등을 받을 수 있다.
◎ 카카오 사용자 정보 가져오기 (메뉴얼)
- 카카오 인가코드 받기 (프론트에서 설정)
https://kauth.kakao.com/oauth/authorize?client_id=본인의 REST API키&redirect_uri={REDIRECT_URI}&response_type=code
- 인가코드 받을 시 처리
@GetMapping("/user/kakao/callback")
public String kakaoLogin(@RequestParam String code) {
// authorizedCode: 카카오 서버로부터 받은 인가 코드
KakaoUserService.kakaoLogin(code); // userService.kakaoLogin 에서 나머지 처리
return "redirect:/";
}
- 카카오 사용자 정보 가져오기
- "인가 코드"로 "액세스 토큰" 요청
public void kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
// 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/user/kakao/callback");
body.add("code", code);
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(body, headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
String accessToken = jsonNode.get("access_token").asText();
}
- "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
// 2. 토큰으로 카카오 API 호출
// HTTP Header 생성
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoUserInfoRequest = new HttpEntity<>(headers);
response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoUserInfoRequest,
String.class
);
responseBody = response.getBody();
jsonNode = objectMapper.readTree(responseBody);
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
String email = jsonNode.get("kakao_account")
.get("email").asText();
System.out.println("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
- 리팩토링 하는 법 (특정 부분을 함수화하여 넘김)
- ' "인가 코드"로 "액세스 토큰" 요청 하는 부분 ' 드래그
- 우클릭 > Refactor > Extract Method (단축키 Ctrl + Alt + M)
- 꼭 동작이 제대로 작동하는지 확인!
- ' "액세스 토큰"으로 "카카오 사용자 정보" 가져오기 ' 부분도 똑같이 Extract Method 가능
◎ 카카오 사용자 정보로 회원가입
-
설계: 사람마다 여러가지로 할 수 있지만, 아래 프로젝트에서는 다음과 같이 실행
- kakaoId Column 추가 : kakao로 회원가입 한 사람은 "kakaoId" 문자열 추가
- password는 UUID (랜덤으로 생성한 문자열)을 인코딩하여 생성
- 기타 나머지 사항은 코드 참고
-
User 테이블에 'kakaoId' 추가 (nullable true, unique true)
-
회원 가입 : kakaoId 를 가진 회원이 없는 경우에만 회원 가입
// DB 에 중복된 Kakao Id 가 있는지 확인
Long kakaoId = kakaoUserInfo.getId();
User kakaoUser = userRepository.findByKakaoId(kakaoId)
.orElse(null); // UserRepository 설정 해주어야 함
if (kakaoUser == null) {
// 회원가입
// username: kakao nickname
String nickname = kakaoUserInfo.getNickname();
// password: random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
// email: kakao email
String email = kakaoUserInfo.getEmail();
// role: 일반 사용자
UserRoleEnum role = UserRoleEnum.USER;
kakaoUser = new User(nickname, encodedPassword, email, role, kakaoId);
userRepository.save(kakaoUser);
}
- 강제 로그인 처리
- "로그인 성공 사용자 정보" (UserDetails) 는 SecurityContext 에 저장됨
- SecurityContextHolder 를 통해 SecurityContext 에 "로그인 성공 사용자 정보" 직접 추가
- 구현
// 4. 강제 로그인 처리 UserDetails userDetails = new UserDetailsImpl(kakaoUser); Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication);
- "로그인 성공 사용자 정보" (UserDetails) 는 SecurityContext 에 저장됨
- 이후 기능별로 리팩토링시, 용이하게 사용할 수 있다.
- KakaoUserService, KakaoUserInfoDto 등으로 파일 분리하는 것도 좋다.
Author And Source
이 문제에 관하여(심화반 - 2주차 - 3), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@gwichanlee/Spring-심화반-2주차-3저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)