[목차여기]
SpringBoot를 이용하여 카카오 로그인을 구현해 보겠습니다.
카카오 로그인에 대해서 설명되어 있는 공식 문서입니다. 공식 문서에 설명이 정말 잘 되어있어서, 꼭 읽어보시길 추천드립니다. 😀
🎯 카카오 로그인이 실행되는 순서
- 프론트가 카카오로부터 인가코드를 발급받는다.
- 프론트가 백에게 인가코드를 전달한다.
- 백은 전달받은 인가코드를 이용하여 카카오로부터 토큰을 발급받는다.
- 백은 토큰을 이용하여 카카오로부터 사용자 정보를 얻는다.
- 백은 사용자 정보를 저장하고, jwt 토큰을 프론트에게 전달한다.
5번은 여러분의 서비스에 맞춰서 하지 않아도 됩니다. 이 글에서는 끝에서 함께 설명하도록 하겠습니다.
프론트에서 인가코드를 발급받는 방법이 일반적이긴 하지만,
로그인 구현이 잘 되었는지 확인하기 위해서 백에서도 인가코드를 발급받는 것을 구현하도록 하겠습니다.
0. 앱 등록하기
카카오에게 내가 만들고자 하는 앱을 등록합니다.
[앱 키]에서 생성된 REST API 키를 확인할 수 있습니다.
카카오에 호출할 때 필요한 키이므로 저는 IntelliJ 환경변수에 미리 넣어두었습니다.
[카카오 로그인]에서 로그인을 활성화합니다.
[카카오 로그인]에서 Redirect URI를 등록합니다.
Redirect URI는 최대 10개를 등록할 수 있습니다. 언제든 수정할 수 있으니 아래의 주소를 하나 등록해 주세요.
http://localhost:8080/api/kakao/callback
[카카오 로그인] > [동의항목]에서 개인정보 동의항목 심사 신청 버튼을 클릭합니다.
그러면 비즈 앱 전환이라는 버튼이 보이고 이것을 클릭합니다.
따로 사업자가 없기 때문에, 개인 개발자 비즈 앱 전환 버튼을 클릭합니다.
전환이 완료되면 다시 [카카오 로그인] > [동의항목]에서 내가 필요한 개인 정보를 선택합니다.
저는 이메일만 필요하기 때문에 이메일을 필수 동의 상태로 설정했습니다.
1. 인가 코드 받기
인가코드를 발급받는 코드는 매우 간단합니다. KakaoLoginController.java 에 코드를 작성해 주세요.
KakaoLoginController.java
@RestController
@RequiredArgsConstructor
@RequestMapping("api/kakao")
public class KakaoLoginController {
@Operation(summary = "인가코드 발급 API")
@GetMapping("/callback")
public ApiResponse<?> callback(@RequestParam("code") String code){
return ApiResponse.onSuccess(Status.KAKAO_CODE_SUCCESS, code);
}
// Swagger와 ApiResponse를 사용하지 않는다면 아래 코드로 작성해주세요.
// 개인적으로 Swagger와 ApiResponse를 사용하는 것을 추천드립니다.
@GetMapping("/callback")
public String callback(@RequestParam("code") String code){
return code;
}
}
❗️확인하기❗️
- Swagger를 사용하지 않으신다면 @Operation 어노테이션 사용이 안되므로 삭제해 주세요.
- ApiResponse는 API 응답 통일성을 위해 제가 만들어 놓은 클래스입니다. 이 글에서는 ApiResponse를 사용해서 설명하겠습니다.
저는 컨트롤러 코드를 작성할 때 응답 통일을 위해서 ApiResponse를 만들어서 공통으로 사용하고 있습니다. 그리고 프론트와의 협업을 위해서 Swagger를 꼭 사용하는데요, 정말 간단하니! 두 가지를 적용하는 것을 추천드립니다.
✅ Swagger : 🔗 Swagger v3 적용하는 법
✅ ApiResponse : (아래 코드 확인)
ApiResponse.java
@Getter
@AllArgsConstructor
@JsonPropertyOrder({"code", "result", "message", "data"})
public class ApiResponse<T> {
private final String code;
private final String result;
private final String message;
@JsonInclude(JsonInclude.Include.NON_NULL)
private T data;
// 성공한 경우 응답 생성
public static <T> ApiResponse<T> onSuccess(Status status, T data){
return new ApiResponse<>(status.getCode(), status.getResult(), status.getMessage(), data);
}
// 실패한 경우 응답 생성
public static <T> ApiResponse<T> onFailure(Status status){
return new ApiResponse<>(status.getCode(), status.getResult(), status.getMessage(), null);
}
// 실패한 경우 응답 생성
public static <T> ApiResponse<T> onFailure(Status status, T data){
return new ApiResponse<>(status.getCode(), status.getResult(), status.getMessage(), data);
}
}
Status.java
@Getter
@RequiredArgsConstructor(access = PRIVATE)
public enum Status {
//예시
TEMP_SUCCESS("200", "SUCCESS", "임시 API 접근에 성공했습니다."),
KAKAO_CODE_SUCCESS("200", "SUCCESS", "카카오 인가코드를 발급받았습니다."),
KAKAO_LOGIN_SUCCESS("200", "SUCCESS", "카카오 로그인을 성공했습니다.");
private final String code;
private final String result;
private final String message;
}
🏁 인가코드 발급 확인하기
아래의 주소로 접속하여 확인합니다. 중괄호{ } 안에 해당하는 값을 넣어주셔야 합니다.
https://kauth.kakao.com/oauth/authorize?client_id={이곳에 REST API 키를 넣어주세요}&redirect_uri={이곳에 redirect_uri를 넣어주세요}&response_type=code
해당 화면이 보이면 성공입니다. 동의 버튼을 누르면 인가코드를 확인할 수 있습니다.
프론트와 백이 연동했다고 가정했을 때, 프론트가 백에게 이 인가코드를 넘겨준다고 생각하시면 됩니다.
지금 저희는 직접 인가코드 발급받는 것을 구현한 것입니다.
2. 토큰 발급받기
토큰을 발급받기 위해서 순서대로 코드를 작성해 주세요.
KakaoLoginDto.java
public class KakaoLoginDto {
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class KakaoTokenResponseDto{
private String access_token;
private String token_type;
private String refresh_token;
private int expires_in;
private String scope;
private int refresh_token_expires_in;
}
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class KakaoUserInfoResponseDto{
private Long id;
private String connected_at;
private KakaoAccount kakao_account;
private Properties properties;
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class KakaoAccount{
private Boolean has_email;
private Boolean email_needs_agreement;
private Boolean is_email_valid;
private Boolean is_email_verified;
private String email;
}
}
}
KakaoTokenResposneDto는 토큰을 발급받을 때 사용하고, KakaoUserInfoResponseDto는 사용자 정보를 읽을 때 사용합니다.
미리 작성해 두겠습니다.
❗️ 주의 ❗️
저는 로그인 시 이메일 값만 필요해서 이메일 값만 추가했습니다. 이메일 값 외에 다른 값을 로그인 동의항목에 추가했다면, KakaoUserInfoResponseDto에도 추가해주셔야 합니다. 속성 이름은 공식문서를 참고하세요.
KakaoLoginService.java
public interface KakaoLoginService {
String getKakaoToken(String code);
KakaoLoginDto.KakaoUserInfoDto getKakaoUserInfo(String token);
}
getKakaoToken()과 getKakaoUserInfo() 함수를 interface 클래스에서 선언합니다.
KakaoLoginServiceImpl.java
@Service
@RequiredArgsConstructor
@Transactional
public class KakaoLoginServiceImpl implements KakaoLoginService{
@Value("${security.kakao.client_id:default_client_id}")
String clientId;
@Value("${security.kakao.redirect_uri:default_redirect_uri}")
String redirectUri;
@Override
public String getKakaoToken(String code) {
RestTemplate tokenRt = new RestTemplate(); // Http
HttpHeaders tokenHeader = new HttpHeaders(); // Http header
tokenHeader.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8"); // key=value
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); // Http body
params.add("grant_type", "authorization_code");
params.add("client_id", clientId);
params.add("redirect_uri", redirectUri);
params.add("code", code);
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest = new HttpEntity<>(params, tokenHeader);
ResponseEntity<String> response = tokenRt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
); // Request to Kakao
ObjectMapper objectMapper = new ObjectMapper();
KakaoLoginDto.KakaoTokenResponseDto tokenResponseDto = new KakaoLoginDto.KakaoTokenResponseDto();
try{
tokenResponseDto = objectMapper.readValue(response.getBody(), KakaoLoginDto.KakaoTokenResponseDto.class);
}catch(JsonMappingException e){
e.printStackTrace();
}catch (JsonProcessingException e){
e.printStackTrace();
}
return tokenResponseDto.getAccess_token();
}
getKakaoToken()을 구현합니다.
clientId와 redirectId 값은 민감 정보이므로 환경변수 처리를 했습니다. 환경변수 처리하는 방법은 위에서 언급했으므로 넘어가겠습니다.
KakaoLoginController.java
@RestController
@RequiredArgsConstructor
@RequestMapping("api/kakao")
public class KakaoLoginController {
private final KakaoLoginService kakaoLoginService;
@Operation(summary = "인가코드 발급 API")
@GetMapping("/callback")
public ApiResponse<?> callback(@RequestParam("code") String code){
return ApiResponse.onSuccess(Status.KAKAO_CODE_SUCCESS, code);
}
// 계속해서 업데이트
@Operation(summary = "프론트로부터 카카오 인가코드 전달받기")
@Parameter(name = "code", description = "카카오에서 받은 인카코드, RequestParam")
@PostMapping("/login")
public ApiResponse<?> kakaoLoginCode(@RequestParam("code") String code) {
String token = kakaoLoginService.getKakaoToken(code);
return ApiResponse.onSuccess(Status.KAKAO_LOGIN_SUCCESS, token);
}
}
kakaoLoginCode를 추가했습니다. 이 컨트롤러는 글을 진행하며 계속해서 업데이트하겠습니다.
🏁 토큰 발급 확인하기
발급받은 인가코드를 복사합니다.
❗️ 주의 : 한 번 사용한 인가코드는 오류가 발생할 수도 있으니 다시 링크에 접속하셔서 재발급받는 것을 추천드립니다.
https://kauth.kakao.com/oauth/authorize?client_id={이곳에 REST API 키를 넣어주세요}&redirect_uri={이곳에 redirect_uri를 넣어주세요}&response_type=code
그리고 Swagger에 접속합니다. Swagger v3는 아래의 주소입니다.
http://localhost:8080/swagger-ui/index.html#/
data에 토큰값이 잘 담기는 것을 확인할 수 있습니다.
3. 사용자 정보 요청
사용자 정보를 요청하기 위해서 순서대로 코드를 업데이트해 주세요.
KakaoLoginServiceImpl.java
@Service
@RequiredArgsConstructor
@Transactional
public class KakaoLoginServiceImpl implements KakaoLoginService{
@Value("${security.kakao.client_id:default_client_id}")
String clientId;
@Value("${security.kakao.redirect_uri:default_redirect_uri}")
String redirectUri;
@Override
public String getKakaoToken(String code) {
// (생략 : 2번에서 구현했습니다.)
}
@Override
public KakaoLoginDto.KakaoUserInfoDto getKakaoUserInfo(String token) {
RestTemplate userRt = new RestTemplate(); // Http
HttpHeaders userHeader = new HttpHeaders(); // Http header
userHeader.add("Authorization", "Bearer " + token); // Bearer + 토큰
userHeader.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
HttpEntity<MultiValueMap<String, String>> kakaoProfileRequest = new HttpEntity<>(userHeader);
ResponseEntity<String> response = userRt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoProfileRequest,
String.class
);
ObjectMapper objectMapper = new ObjectMapper();
KakaoLoginDto.KakaoUserInfoResponseDto kakaoUserInfoResponseDto = new KakaoLoginDto.KakaoUserInfoResponseDto();
try{
kakaoUserInfoResponseDto = objectMapper.readValue(response.getBody(), KakaoLoginDto.KakaoUserInfoResponseDto.class);
}catch(JsonMappingException e){
e.printStackTrace();
}catch (JsonProcessingException e){
e.printStackTrace();
}
KakaoLoginDto.KakaoUserInfoDto dto = new KakaoLoginDto.KakaoUserInfoDto().builder()
.email(kakaoUserInfoResponseDto.getKakao_account().getEmail())
.build();
return dto;
}
}
KakaoLoginController.java
@RestController
@RequiredArgsConstructor
@RequestMapping("api/kakao")
public class KakaoLoginController {
private final KakaoLoginService kakaoLoginService;
@Operation(summary = "인가코드 발급 API")
@GetMapping("/callback")
public ApiResponse<?> callback(@RequestParam("code") String code){
return ApiResponse.onSuccess(Status.KAKAO_CODE_SUCCESS, code);
}
@Operation(summary = "프론트로부터 카카오 인가코드 전달받기")
@Parameter(name = "code", description = "카카오에서 받은 인카코드, RequestParam")
@PostMapping("/login")
public ApiResponse<?> kakaoLoginCode(@RequestParam("code") String code) {
String token = kakaoLoginService.getKakaoToken(code);
KakaoLoginDto.KakaoUserInfoDto kakaoUserInfoDto = kakaoLoginService.getKakaoUserInfo(token);
return ApiResponse.onSuccess(Status.KAKAO_LOGIN_SUCCESS, kakaoUserInfoDto);
}
}
🏁 사용자 정보 확인하기
인가코드를 복사합니다.
https://kauth.kakao.com/oauth/authorize?client_id={이곳에 REST API 키를 넣어주세요}&redirect_uri={이곳에 redirect_uri를 넣어주세요}&response_type=code
그리고 Swagger에 접속합니다.
http://localhost:8080/swagger-ui/index.html#/
data에 사용자 정보가 담긴 것을 확인할 수 있습니다.
저는 이메일만 로그인 동의항목에 추가했기 때문에 이메일 값 하나만 보이는 것입니다.
3. 사용자 정보 저장 & JWT 토큰 발급
지금부터는 여러분의 서비스에 맞춰서 구현이 필요합니다.
사용자 정보를 저장하는 방법은 일반 회원가입 구현할 때와 동일합니다.
JWT 토큰 발급에 대한 자세한 설명은 이 글에서 하지 않겠습니다. 글이 너무 길어질 거 같아요 😭
대신 코드는 모두 작성해 두었습니다.
📌 구현 순서
1. 사용자 정보(이메일)를 이용하여 이미 회원가입이 된 사용자인지 확인한다.
1-1. 회원가입이 되어 있지 않다면 데이터베이스에 저장한 뒤, 로그인하고 jwt 토큰을 발급한다.
1-2. 회원가입이 이미 되어 있다면, 로그인하고 jwt 토큰을 발급한다.
📌 구현에 필요한 클래스
User.java ➡️ user 테이블을 구현한 entity입니다.
UserCustomDto.java ➡️ 사용자 인증에 필요한 정보를 담습니다.
KakaoLoginDto.java ➡️ (위에서 사용함) 사용자 정보를 읽을 때 필요합니다.
JWTUtil.java ➡️ JWT 토큰 생성, 파싱, 검증 기능을 수행합니다.
AppLoginFilter.java ➡️ 로그인 요청 시 인증을 처리하는 필터입니다.
JWTFilter.java ➡️ 사용자 요청이 들어올 때 JWT 토큰을 검증하는 필터입니다.
SecurityConfig.java ➡️ Spring Security의 보안 설정을 담당합니다.
Build.gradle
dependencies {
// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' // JSON 파싱을 위해 Jackson 사용
implementation 'org.springframework.boot:spring-boot-starter-security'
}
JWT를 사용하기 위해서 Build.gradle에 코드를 추가해 주세요.
UserCustomDto.java
public class UserCustomDto implements UserDetails {
private final User user;
public UserCustomDto(User user) {this.user = user;}
@Override
public Collection<? extends GrantedAuthority> getAuthorities(){
Collection<GrantedAuthority> collection = new ArrayList<>();
collection.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return null;
}
});
return collection;
}
@Override
public String getPassword() { return user.getEmail(); }
@Override
public String getUsername() { return user.getEmail(); }
public Long getUserId(){
return user.getId();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
KakaoLoginDto.java
public class KakaoLoginDto {
// (생략) 위에서 구현함
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class KakaoUserInfoDto{ // 카카오 토큰을 이용해서 읽어온 유저 정보
private String email;
}
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class LoginResponseDto{
private Long userId;
private String token;
}
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class LoginRequestDto{
public String email;
}
}
KakaoLoginDto는 이미 생성한 클래스인데요, 3개의 static class를 추가해 줍니다.
JWTUtil.java
@Component
public class JWTUtil {
private SecretKey secretKey;
public JWTUtil(@Value("${spring.jwt.secret}")String secret) {
this.secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
}
public Long getUserId(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
return claims.get("userId", Long.class);
}
public Boolean isExpired(String token) {
Date expiration = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody()
.getExpiration();
return expiration.before(new Date());
}
public String createJwt(Long userId, String nickname, Long expiredMs) {
return Jwts.builder()
.claim("userId", userId)
.claim("nickname", nickname)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiredMs))
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}
}
secretKey 값은 환경변수 처리를 해주었습니다.
secretKey 변수 값으로는 그냥 아주 어렵고 긴 값을 마음대로 넣어주세요.
예시 : dksfhak;jsdjflks1234323948758493029847rdsmnkfaknlsdmfalknflk;samdnklga;g0294758932903
JWTFilter.java
public class JWTFilter extends OncePerRequestFilter {
private final JWTUtil jwtUtil;
public JWTFilter(JWTUtil jwtUtil){
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authorization = request.getHeader("Authorization");
if(authorization == null || !authorization.startsWith("Bearer ")){
filterChain.doFilter(request, response);
return; //조건이 해당되면 메소드 종료(필수)
}
String token = authorization.split(" ")[1]; //Bearer 부분 제거 후 순수 토큰만 획득
if (jwtUtil.isExpired(token)) {
filterChain.doFilter(request, response);
return; // 조건이 해당되면 메소드 종료 (필수)
}
Long userId = jwtUtil.getUserId(token);
User user = User.builder()
.id(userId)
.build();
UserCustomDto userCustomDto = new UserCustomDto(user);
Authentication authToken = new UsernamePasswordAuthenticationToken(userCustomDto, null, userCustomDto.getAuthorities()); //스프링 시큐리티 인증 토큰 생성
SecurityContextHolder.getContext().setAuthentication(authToken); //세션에 사용자 등록
filterChain.doFilter(request, response);
}
}
AppLoginFilter.java
public class AppLoginFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
//JWTUtil 주입
private final JWTUtil jwtUtil;
public AppLoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil){
this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
ObjectMapper objectMapper = new ObjectMapper();
KakaoLoginDto.LoginRequestDto loginRequest = objectMapper.readValue(request.getInputStream(), KakaoLoginDto.LoginRequestDto.class);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
loginRequest.getEmail(),
loginRequest.getEmail() // 비밀번호 대신 이메일 사용
);
return authenticationManager.authenticate(authToken);
} catch (IOException e) {
throw new RuntimeException("Error reading login request", e);
}
}
// 로그인 성공시 실행하는 메소드 -> 여기서 JWT가 발급됨.
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) {
UserCustomDto userCustomDto = (UserCustomDto) authentication.getPrincipal();
String token = jwtUtil.createJwt(userCustomDto.getUserId(), 60 * 60 * 1000L); // 만료 시간 1시간 설정
response.addHeader("Authorization", "Bearer " + token);
}
// 로그인 실패시 실행하는 메소드
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
try {
response.getWriter().write("{\"error\": \"Invalid login credentials\"}");
} catch (IOException e) {
e.printStackTrace();
}
}
}
카카오 로그인은 소셜 로그인이므로 따로 회원가입 시 비밀번호를 생성하지 않습니다.
본인 서비스 로직을 어떻게 설계하느냐에 따라 다를 수 있습니다.
그런데 jwt 토큰을 발급하려면 무조건 비밀번호가 필요해서, 저는 비밀번호 대신 이메일을 사용했습니다.
만약 일반 로그인을 구현할 때 jwt 토큰을 발급한다면 진짜 비밀번호를 넣어주면 되는 것입니다.
SecurityConfig.java
@Configuration
public class SecurityConfig {
private static final String[] PERMIT_URL_ARRAY = {
/* swagger v3 */
"/v3/api-docs/**",
"/swagger-ui/**"
};
private final AuthenticationConfiguration authenticationConfiguration;
private final JWTUtil jwtUtil;
public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil) {
this.authenticationConfiguration = authenticationConfiguration;
this.jwtUtil = jwtUtil;
}
//AuthenticationManager Bean 등록
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf((auth) -> auth.disable());
http.formLogin((auth) -> auth.disable());
http.httpBasic((auth) -> auth.disable());
http.authorizeHttpRequests((auth) -> auth
.requestMatchers("/**", "/").permitAll()
.requestMatchers(PERMIT_URL_ARRAY).permitAll()
.anyRequest().authenticated());
http.addFilterAt(new AppLoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class); //JWTUtil 인수 추가
http.addFilterAt(new AppLoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new JWTFilter(jwtUtil), AppLoginFilter.class);
http.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
KakaoLoginController.java
@RestController
@RequiredArgsConstructor
@RequestMapping("api/kakao")
public class KakaoLoginController {
private final KakaoLoginService kakaoLoginService;
private final UserAccountService userAccountService;
private final JWTUtil jwtUtil;
@Operation(summary = "인가코드 발급 API")
@GetMapping("/callback")
public ApiResponse<?> callback(@RequestParam("code") String code){
return ApiResponse.onSuccess(Status.KAKAO_CODE_SUCCESS, code);
}
@Operation(summary = "프론트로부터 카카오 인가코드 전달받기")
@Parameter(name = "code", description = "카카오에서 받은 인카코드, RequestParam")
@PostMapping("/login")
public ApiResponse<?> kakaoLoginCode(@RequestParam("code") String code) {
String token = kakaoLoginService.getKakaoToken(code);
KakaoLoginDto.KakaoUserInfoDto kakaoUserInfoDto = kakaoLoginService.getKakaoUserInfo(token);
// 이미 회원가입된 사용자인지 확인한다.
if(userAccountService.findByEmail(kakaoUserInfoDto.getEmail()).isEmpty()) userAccountService.saveKakaoUser(kakaoUserInfoDto.getEmail());
User user = userAccountService.findByEmail(kakaoUserInfoDto.getEmail()).get();
// JWT 토큰 발급
String jwtToken = jwtUtil.createJwt(user.getId(), 3600000L);
KakaoLoginDto.LoginResponseDto dto = new KakaoLoginDto.LoginResponseDto().builder()
.userId(user.getId())
.token(jwtToken)
.build();
return ApiResponse.onSuccess(Status.LOGIN_SUCCESS, dto);
}
}
마지막으로 Controller를 구현하면 끝입니다!!! 🤤🤤🤤
이미 회원가입된 사용자인지 확인하는 로직은 일반 회원가입과 동일합니다.
긴 글 읽어주셔서 감사합니다 😊
'Tutorial' 카테고리의 다른 글
[SpringBoot] GitHub Actions, AWS, Docker를 이용한 CI/CD 구축하는 법 (1) | 2024.11.19 |
---|