스프링 부트와 AWS로 혼자 구현하는 웹 서비스
라는 책이 있는데, 이를 보고 OAuth2를 이용해서 구현해주는 방법도 있지만, 이걸 사용하진 않았다. (이상하게 리다이렉션 횟수가 너무 많습니다..
라는 Error가 발생했다. 지금와서 생각해보니 아래의 문제상황에서 코드 작성을 시도했던지라 해결 방법 또한 제대로 떠올리지 못했던 것 같은데, 어쨌든 도저히 문제해결이 되질 않아서 다른 방법을 찾아 나섰다.)kakao developers
에서 코드 흐름을 이해하고, 필요한 request, response 들을 하나씩 뜯어서 이해하다보니, 그제서야 다른 사람들의 코드들도 제대로 눈에 들어오고, 내가 어떤걸 가져다쓰고 버려야할지 정리가 되기 시작했다.implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'
implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1'
import com.daily.themoti.user.constant.UserRole;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@NoArgsConstructor
@Getter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String email;
@Column
private String username;
@Enumerated(EnumType.STRING)
private UserRole role;
@Builder
public User(String username, String email, UserRole role){
this.username = username;
this.email = email;
this.role = role;
}
}
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum UserRole {
USER("USER", "사용자"),
GUEST("GUEST", "손님");
private final String key;
private final String title;
}
import com.daily.themoti.user.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/login/{token}")
public void login(@PathVariable String token){
userService.loginWithAccessToken(token);
}
@GetMapping("/user/kakao/oauth") // 카카오 어플리케이션에서 설정해준 redirect uri이다. code를 가져온 후 access_token 을 리턴
public String getCode(@RequestParam String code){
return userService.getAccessToken(code);
}
}
findByEmail
을 작성했다.
import com.daily.themoti.user.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
UserService
public interface UserService {
void loginWithAccessToken(String token);
String getAccessToken(String code);
}
UserServiceImpl
import com.daily.themoti.smokearea.exception.ParseFailedException;
import com.daily.themoti.user.dto.KakaoUserInfo;
import com.daily.themoti.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService{
private final UserRepository userRepository;
@Value("${apikey.kakao.rest.api.key}")
private String KAKAO_REST_API_KEY;
@Override
public String getAccessToken(String code){
String kakaoURL = "https://kauth.kakao.com/oauth/token";
String access_token = "";
String refresh_token = "";
try{
URL url = new URL(kakaoURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
StringBuilder sb = new StringBuilder();
sb.append("grant_type=authorization_code");
sb.append("&client_id=" + KAKAO_REST_API_KEY);
sb.append("&redirect_uri=http://localhost:8080/user/kakao/oauth");
sb.append("&code=" + code);
bw.write(sb.toString());
bw.flush();
int responseCode = connection.getResponseCode();
System.out.println("responseCode : " + responseCode);
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = "";
String result = "";
while((line = br.readLine()) != null){
result += line;
}
System.out.println("response body = " + result);
JSONObject object = parseJSON(result);
access_token = (String) object.get("access_token");
refresh_token = (String) object.get("refresh_token");
} catch(IOException e){
e.printStackTrace();
}
return access_token;
}
@Override
public void loginWithAccessToken(String token) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + token);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RestTemplate rt = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> kakaoProfileRequest = new HttpEntity<>(headers);
rt.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
rt.setErrorHandler(new DefaultResponseErrorHandler() {
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = response.getStatusCode();
return statusCode.series() == HttpStatus.Series.SERVER_ERROR;
}
});
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoProfileRequest,
String.class
);
String str = response.getBody();
JSONObject kakao_response = parseJSON(str);
JSONObject kakao_account = (JSONObject) kakao_response.get("kakao_account");
JSONObject profile = (JSONObject) kakao_account.get("profile");
String email = (String) kakao_account.get("email");
String nickname = (String) profile.get("nickname");
KakaoUserInfo kakaoUserInfo = new KakaoUserInfo(email, nickname);
if(!userRepository.findByEmail(email).isPresent()){
userRepository.save(kakaoUserInfo.toEntity());
}
}
private JSONObject parseJSON(String result){
try {
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(result);
return jsonObject;
} catch(ParseException e){
throw new ParseFailedException();
}
}
}
UserService - access token을 통해서 유저 정보를 얻어오는 부분) https://velog.io/@kimujin99/Spring-React-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-JWT-2