https://developers.kakao.com/console/app
카카오 로그인 설정하기
카카오 로그인을 하기 전에 개발자 사이트에서 '어플리케이션 등록'을 해야 합니다.
1. 회원가입 후 어플리케이션 추가
2. 앱 이름, 사업자명 저장
3. 사이트 도메인 등록
- 애플리케이션 선택
- 플랫폼 메뉴 선택
- Web 플랫폼 등록
- 사이트 도메인 입력
4. Redirect URI (callback) 설정
카카오 로그인을 하기위해서 인가토큰을 받게 될 Redirect URI(callback)을 설정해야한다.
5. 동의 항목 설정하기
프로필 정보와 이메일 정보를 체크합니다. 나머지는 사용할 수 가 없습니다..
카카오 로그인 구현하기
인가코드는 REST API키를 사용해야한다.
사용자가 카카오 로그인 페이지를 통해 '동의하고 계속하기'를 클릭하면, 미리 설정해둔 Redirect URI (callback) 로 인가토큰이 전달 됩니다.
Controller
@GetMapping("/user/kakao/callback")
public String kakaoLogin(String code) {
// authorizedCode: 카카오 서버로부터 받은 인가 코드
userService.kakaoLogin(code);
return "redirect:/";
}
Service
@Service
public class UserService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final KakaoOAuth2 kakaoOAuth2;
private final AuthenticationManager authenticationManager;
private static final String ADMIN_TOKEN = "AAABnv/xRVklrnYxKZ0aHgTBcXukeZygoC";
@Autowired
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, KakaoOAuth2 kakaoOAuth2, AuthenticationManager authenticationManager) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.kakaoOAuth2 = kakaoOAuth2;
this.authenticationManager = authenticationManager;
}
public void kakaoLogin(String authorizedCode) {
// 카카오 OAuth2 를 통해 카카오 사용자 정보 조회
KakaoUserInfo userInfo = kakaoOAuth2.getUserInfo(authorizedCode);
Long kakaoId = userInfo.getId();
String nickname = userInfo.getNickname();
String email = userInfo.getEmail();
// 우리 DB 에서 회원 Id 와 패스워드
// 회원 Id = 카카오 nickname
String username = nickname;
// 패스워드 = 카카오 Id + ADMIN TOKEN
String password = kakaoId + ADMIN_TOKEN;
// DB 에 중복된 Kakao Id 가 있는지 확인
User kakaoUser = userRepository.findByKakaoId(kakaoId).orElse(null);
// 카카오 정보로 회원가입
if (kakaoUser == null) {
// 패스워드 인코딩
String encodedPassword = passwordEncoder.encode(password);
// ROLE = 사용자
UserRole role = UserRole.USER;
kakaoUser = new User(nickname, encodedPassword, email, role, kakaoId);
userRepository.save(kakaoUser);
}
// 로그인 처리
Authentication kakaoUsernamePassword = new UsernamePasswordAuthenticationToken(username, password);
Authentication authentication = authenticationManager.authenticate(kakaoUsernamePassword);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
Respository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Optional<User> findByKakaoId(Long kakaoId);
}
Domain
카카오 아이디 추가
@Setter
@Getter // get 함수를 일괄적으로 만들어줍니다.
@NoArgsConstructor // 기본 생성자를 만들어줍니다.
@Entity // DB 테이블 역할을 합니다.
public class User extends Timestamped {
public User(String username, String password, String email, UserRole role) {
this.username = username;
this.password = password;
this.email = email;
this.role = role;
this.kakaoId = null;
}
public User(String username, String password, String email, UserRole role, Long kakaoId) {
this.username = username;
this.password = password;
this.email = email;
this.role = role;
this.kakaoId = kakaoId;
}
// ID가 자동으로 생성 및 증가합니다.
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
private Long id;
// 반드시 값을 가지도록 합니다.
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRole role;
@Column(nullable = true)
private Long kakaoId;
}
AuthenticationManager 빈 등록
> security > WebSecurityConfig
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
DTO(Userinfo)
security > kakao > KakaoUserInfo
@AllArgsConstructor
@Getter
public class KakaoUserInfo {
Long id;
String email;
String nickname;
}
security > kakao > KakaoOAuth2
@Component
public class KakaoOAuth2 {
public KakaoUserInfo getUserInfo(String authorizedCode) {
// 1. 인가코드 -> 액세스 토큰
String accessToken = getAccessToken(authorizedCode);
// 2. 액세스 토큰 -> 카카오 사용자 정보
KakaoUserInfo userInfo = getUserInfoByToken(accessToken);
return userInfo;
}
private String getAccessToken(String authorizedCode) {
// HttpHeader 오브젝트 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HttpBody 오브젝트 생성
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code");
params.add("client_id", "{본인의 REST API키}");
params.add("redirect_uri", "http://localhost:8080/user/kakao/callback");
params.add("code", authorizedCode);
// HttpHeader와 HttpBody를 하나의 오브젝트에 담기
RestTemplate rt = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(params, headers);
// Http 요청하기 - Post방식으로 - 그리고 response 변수의 응답 받음.
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
// JSON -> 액세스 토큰 파싱
String tokenJson = response.getBody();
JSONObject rjson = new JSONObject(tokenJson);
String accessToken = rjson.getString("access_token");
return accessToken;
}
private KakaoUserInfo getUserInfoByToken(String accessToken) {
// HttpHeader 오브젝트 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HttpHeader와 HttpBody를 하나의 오브젝트에 담기
RestTemplate rt = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> kakaoProfileRequest = new HttpEntity<>(headers);
// Http 요청하기 - Post방식으로 - 그리고 response 변수의 응답 받음.
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoProfileRequest,
String.class
);
JSONObject body = new JSONObject(response.getBody());
Long id = body.getLong("id");
String email = body.getJSONObject("kakao_account").getString("email");
String nickname = body.getJSONObject("properties").getString("nickname");
return new KakaoUserInfo(id, email, nickname);
}
}
'Spring > Spring Boot' 카테고리의 다른 글
[Spring] JPA의 이해(JPA, Hibernate) (0) | 2021.12.29 |
---|---|
[Spring] 스프링 부트 페이지네이션 (Query, JPA, offset / cursor 페이지네이션) (2) | 2021.12.23 |
[Spring] 스프링 시큐리티 사용하기 - spring security (로그인, 로그아웃, 회원가입) (0) | 2021.12.04 |
[Spring] 웹의 인증,인가 / 쿠키와 세션 (0) | 2021.12.03 |
Spring Putmapping Json 과 File 데이터 함께 전송하기(Form data) (6) | 2021.12.03 |