728x90
반응형
SMALL
카카오 OAuth2 로그인 Setting
- 위 링크는 카카오 로그인 공식문서
- 카카오 OAuth2 로그인의 흐름 및 카카오 로그인을 구현할 때 필수적으로 지켜야할 규약 등을 볼 수 있다.
API Key 받기
- 카카오 로그인 후 애플리케이션을 추가해준다.
- 애플리케이션 추가하기 선택
- 앱 이름, 사업자 명 을 작성하고 카테고리는 "교육" 으로 체크박스에 동의하고 저장
- 등록한 애플리케이션을 선택
로그인 활성화
- 이 화면에서 활성화 설정을 ON 해준다.
동의 항목 설정
- 메뉴바에서 카카오 로그인 밑에 동의 항목으로 이동
- 개인정보 동의 항목 심사 신청을 신청하지 않으면 받을 수 있는 사용자의 정보는 닉네임과 프로필 사진 뿐이다.
- 설정을 눌러 동의 해주면 된다.
- 여기서 필수 등록, 선택 동의 이용 중 동의 등을 할 수 있고 동의 목적을 필수 적으로 작성해주어야 한다.
- 본인은 "학습용" 이라고 작성하였음
- 상태가 바뀐걸 확인 할 수 있음
- 그 다음 메뉴바에서 플랫폼을 선택 하고 이동
- 해당 페이지에서 Web플랫폼 등록 선택
- 로컬에서 돌린다면 localhost로 지정
- 혹은 127.0.0.1로 지정
- 배포하는 도메인이 있다면 도메인을 작성
RedirectURL 설정
- 로그인 요청이 들어와서 승인 후 Redirect 될 주소를 적어주어야 한다.
- 아까 로그인 활성화를 했던 페이지로 이동 (메뉴바에서 로그인)
- 맨 밑으로 내리면 RedirectURL 등록 버튼
- 본인은 http://localhost:8080/oauth2/kakao/callback 로 설정 해 주었고
- 만약 https로 배포하였다면 https로 적어주어야 한다.
카카오 OAuth2 로그인 To Spring
application.yml
Controller
@Controller
@RequestMapping("/oauth2")
public class OAuth2Controller {
// kakao 로그인
@Value("${app.oauth2.kakao.client-id}")
private String kakaoClientId;
@Value("${app.oauth2.kakao.redirect-uri}")
private String kakaoRedirectUri;
@Value("${app.oauth2.kakao.token-uri}")
private String kakaoTokenUri;
@Value("${app.oauth2.kakao.user-info-uri}")
private String kakaoUserInfoUri;
@Value("${app.oauth2.password}")
private String oauth2Password;
@Autowired
private UserService userService;
@Autowired
private AuthenticationManager authenticationManager;
// Kakao 인증 처리 callback
@GetMapping("/kakao/callback")
public String kakaoCallBack(String code, Model model){ // Kakao 가 보내준 code 값 받아오기
//------------------------------------------------------------------
// ■ code 값 확인
// code 값을 받았다는 것은 인증 완료 되었다는 뜻..
//System.out.println("\n<<카카오 인증 완료>>\ncode: " + code);
//----------------------------------------------------------------------
// ■ Access token 받아오기 <= code 값 사용
// 이 Access token 을 사용하여 Kakao resource server 에 있는 사용자 정보를 받아오기 위함.
KakaoOAuthToken token = kakaoAccessToken(code);
//------------------------------------------------------------------
// ■ 사용자 정보 요청 <= Access Token 사용
KakaoProfile profile = kakaoUserInfo(token.getAccess_token());
//---------------------------------------------------
// ■ 회원가입 시키기 <= KakaoProfile (사용자 정보) 사용 => user 객체 전달하기
//User kakaoUser = registerKakaoUser(profile);
//---------------------------------------------------
// ■ 로그인 처리
//loginKakaoUser(kakaoUser);
//기회원자인지 확인
User kakaoUser = kakaoUserTemp(profile);
if(kakaoUser != null){ //회원 정보가 없을 경우
model.addAttribute("info", kakaoUser);
return "user/login";
}
return "redirect:/";
}
@PostMapping("/register")
public String registerOk(@ModelAttribute User kakaoUser, Model model){
/* System.out.println("""
[카카오 인증 회원 정보]
username: %s
name: %s
password: %s
provider: %s
providerId: %s
""".formatted(kakaoUser.getUsername(), kakaoUser.getName(), kakaoUser.getPassword(), kakaoUser.getProvider(), kakaoUser.getProviderId()));*/
int cnt = userService.register(kakaoUser); // 회원 가입!
User user = null;
if(cnt > 0){
//System.out.println("[Kakao 인증 회원 가입 성공]");
user = userService.findByUsername(kakaoUser.getUsername());
} else {
//System.out.println("[Kakao 인증 회원 가입 실패]");
}
loginKakaoUser(user); //로그인
return "redirect:/";
}
// 로그인 진행
private void loginKakaoUser(User kakaoUser) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
kakaoUser.getUsername(),
oauth2Password
);
Authentication authentication = authenticationManager.authenticate(authenticationToken);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(authentication);
U.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);
//System.out.println("Kakao 인증 로그인 처리 완료");
}
//기회원자인지 확인하기 위한 User찾기
private User kakaoUserTemp(KakaoProfile profile) {
String provider = "KAKAO";
String providerId = "" + profile.getId();
String username = provider + "_" + providerId;
String name = profile.getKakaoAccount().getProfile().getNickname();
String password = oauth2Password;
User user = userService.findByUsername(username);
User newUser = null;
if (user == null) { // 미가입자 인 경우 회원 가입 진행
newUser = User.builder()
.username(username)
.name(name)
.password(password)
.provider(provider)
.providerId(providerId)
.build();
} else {
loginKakaoUser(user);
}
return newUser;
}
//
//-----------------------------------------------------------------------------
// 회원가입 시키기 (username, password, name 필요)
// Kakao 로그인 한 회원을 User 에 등록하기
private User registerKakaoUser(KakaoProfile profile) {
// 새로이 가입시킬 username 을 생성 (unique 해야 한다!)
String provider = "KAKAO";
String providerId = "" + profile.getId();
String username = provider + "_" + providerId;
String name = profile.getKakaoAccount().getProfile().getNickname();
String password = oauth2Password;
/*System.out.println("""
[카카오 인증 회원 정보]
username: %s
name: %s
password: %s
provider: %s
providerId: %s
""".formatted(username, name, password, provider, providerId));*/
// 회원 가입 진행하기 전에
// 이미 가입한 회원인지, 혹은 비가입자인지 체크하여야 한다
User user = userService.findByUsername(username);
if(user == null){ // 미가입자 인 경우 회원 가입 진행
User newUser = User.builder()
.username(username)
.name(name)
.password(password)
.provider(provider)
.providerId(providerId)
.build();
int cnt = userService.register(newUser); // 회원 가입!
if(cnt > 0){
//System.out.println("[Kakao 인증 회원 가입 성공]");
user = userService.findByUsername(username); // 다시 읽어오기, regDate 정보
} else {
//System.out.println("[Kakao 인증 회원 가입 실패]");
}
} else {
//System.out.println("[Kakao 인증. 이미 가입된 회원입니다]");
}
return user;
} // end registerKakaoUser()
// Kakao 사용자 정보 요청하기
private KakaoProfile kakaoUserInfo(String accessToken) {
RestTemplate rt = new RestTemplate();
// header 준비
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// body 는 필요없다. 위 header 만 담은 HttpEntity 생성
HttpEntity<MultiValueMap<String, String>> kakaoProfileRequest =
new HttpEntity<>(headers);
// 요청!
ResponseEntity<String> response = rt.exchange(
kakaoUserInfoUri,
HttpMethod.POST,
kakaoProfileRequest,
String.class
);
//System.out.println("카카오 사용자 Profile 요청 응답: " + response);
//System.out.println("카카오 사용자 Profile 응답 : " + response.getBody());
// 사용자 정보 JSON -> Java 로 받아내기
ObjectMapper mapper = new ObjectMapper();
KakaoProfile profile = null;
try {
profile = mapper.readValue(response.getBody(), KakaoProfile.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
// 확인
/*System.out.println("""
[카카오 회원정보]
id: %s
nickname: %s
""".formatted(profile.getId(), profile.getKakaoAccount().getProfile().getNickname()));*/
return profile;
}
// Kakao Access Token 받아오기
public KakaoOAuthToken kakaoAccessToken(String code){
// POST 방식으로 key-value 형식으로 데이터 요청 (카카오 서버 쪽으로!)
RestTemplate rt = new RestTemplate();
// header 준비 (HttpHeader)
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// body 데이터 준비 (HttpBody)
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code");
params.add("client_id", kakaoClientId);
params.add("redirect_uri", kakaoRedirectUri);
params.add("code", code); // 인증 직후 받은 code 값 사용!
// 위 header 와 body 를 담은 HttpEntity 생성
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(params, headers);
// 요청!
ResponseEntity<String> response = rt.exchange(
kakaoTokenUri, // Access Token 요청 uri
HttpMethod.POST, // request method
kakaoTokenRequest, // HttpEntity (body + header)
String.class // 응답받을 타입
);
// System.out.println("카카오 AccessToken 요청 응답: " + response);
//System.out.println("카카오 AccessToken 응답 body: " + response.getBody());
// 응답받은 Json -> Java Object
ObjectMapper mapper = new ObjectMapper();
KakaoOAuthToken token = null;
try {
token = mapper.readValue(response.getBody(), KakaoOAuthToken.class);
// 확인
//System.out.println("카카오 AccessToken: " + token.getAccess_token());
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return token;
}
} // end Controller
- 해당 Controller는 내가 지정한 CallBack 함수에 대해 카카오 서버로 부터 Token을 받고 회원 가입을 하거나 로그인을 할 수 있도록 구상 및 구현
유저의 도메인, 서비스, Repository 등 의 로그인 동작 구현 및 구상 관련 내용은
아래 링크를 통해 확인
728x90
반응형
LIST