Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: 카카오로그아웃 구현 #68

Merged
merged 6 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/main/java/jeje/work/aeatbe/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/api/article/likes/**");
.addPathPatterns("/api/article/likes/**")
.addPathPatterns("/api/users/logout/**")
. addPathPatterns("/api/wishlist/**");
}

@Override
Expand Down
44 changes: 40 additions & 4 deletions src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import jeje.work.aeatbe.annotation.LoginUser;
import jeje.work.aeatbe.domian.KakaoProperties;
import jeje.work.aeatbe.domian.KakaoTokenResponsed;
import jeje.work.aeatbe.dto.user.TokenResponseDto;
import jeje.work.aeatbe.dto.Kakao.LogoutResponseDto;
import jeje.work.aeatbe.dto.Kakao.TokenResponseDto;
import jeje.work.aeatbe.service.KakaoService;
import jeje.work.aeatbe.service.UserService;
import jeje.work.aeatbe.utility.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -26,19 +29,52 @@ public class KakaoAuthController {
private final KakaoProperties kakaoProperties;
private final KakaoService kakaoService;

/**
* 카카오 로그인페이지로 리다이렉션
* @param response
* @throws IOException
*/
@GetMapping("/login")
public void redirectKakaoLogin(HttpServletResponse response) throws IOException {
String url = kakaoProperties.authUrl() +
"?scope=talk_message&response_type=code&client_id=" + kakaoProperties.clientId() +
"?scope=talk_message,profile_nickname&response_type=code&client_id=" + kakaoProperties.clientId() +
"&redirect_uri=" + kakaoProperties.redirectUrl();
response.sendRedirect(url);
}

/**
* 카카오 로그인후 jwt토큰 발급
* @param code
* @return
*/
@GetMapping("/callback")
public ResponseEntity<TokenResponseDto> getAccessToken(@RequestParam String code){
KakaoTokenResponsed token = kakaoService.getKakaoTokenResponse(code);
String jwt = kakaoService.Login(token.accessToken(), token.refreshToken());
return new ResponseEntity<>(new TokenResponseDto(jwt), HttpStatus.OK);
String jwt = kakaoService.login(token.accessToken(), token.refreshToken());
return ResponseEntity.ok(new TokenResponseDto(jwt));
}

/**
* 카카오 로그아웃후 카카오계정과 함께 로그아웃으로 리다이렉션
* @param response
* @param userid
* @throws IOException
*/
@PostMapping("/logout")
public void logout(HttpServletResponse response, @LoginUser Long userid) throws IOException{
String url = kakaoProperties.logoutUrl() +
"?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl();
LogoutResponseDto logoutResponseDto = kakaoService.logout(userid);
response.sendRedirect(url);
}

/**
* 카카오계정과 함께 로그아웃
* @return 카카오계정과 함꼐 로그아웃 페이지
*/
@GetMapping("/logoutWithKakao/callback")
public ResponseEntity<?> logoutWithKakao(){
return ResponseEntity.ok().build();
}

}
24 changes: 24 additions & 0 deletions src/main/java/jeje/work/aeatbe/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package jeje.work.aeatbe.controller;

import jeje.work.aeatbe.annotation.LoginUser;
import jeje.work.aeatbe.dto.user.UserInfoResponseDto;
import jeje.work.aeatbe.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

@GetMapping("/info")
public ResponseEntity<UserInfoResponseDto> getUserInfo(@LoginUser Long userId){
UserInfoResponseDto userInfoResponseDto = userService.getUserInfo(userId);
return ResponseEntity.ok(userInfoResponseDto);
}
}
10 changes: 10 additions & 0 deletions src/main/java/jeje/work/aeatbe/domian/KakaoAccount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package jeje.work.aeatbe.domian;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record KakaoAccount(
Profile profile
) {
}
4 changes: 3 additions & 1 deletion src/main/java/jeje/work/aeatbe/domian/KakaoProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
public record KakaoProperties(
String clientId,
String redirectUrl,
String authUrl
String authUrl,
String logoutUrl,
String logoutRedirectUrl
) {

}
8 changes: 7 additions & 1 deletion src/main/java/jeje/work/aeatbe/domian/KakaoUserInfo.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package jeje.work.aeatbe.domian;

public record KakaoUserInfo(Long id) {
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record KakaoUserInfo(
Long id,
KakaoAccount kakaoAccount
) {
}
10 changes: 10 additions & 0 deletions src/main/java/jeje/work/aeatbe/domian/Profile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package jeje.work.aeatbe.domian;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record Profile(
String nickname
){
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package jeje.work.aeatbe.dto.Kakao;

public record LogoutResponseDto(
long id
) {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package jeje.work.aeatbe.dto.user;
package jeje.work.aeatbe.dto.Kakao;

import lombok.Builder;

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/jeje/work/aeatbe/dto/user/UserInfoResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package jeje.work.aeatbe.dto.user;

import lombok.Builder;

@Builder
public record UserInfoResponseDto(
Long id,
String userName,
String userImageUrl
) {
}
51 changes: 44 additions & 7 deletions src/main/java/jeje/work/aeatbe/service/KakaoService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import jeje.work.aeatbe.domian.KakaoProperties;
import jeje.work.aeatbe.domian.KakaoTokenResponsed;
import jeje.work.aeatbe.domian.KakaoUserInfo;
import jeje.work.aeatbe.dto.Kakao.LogoutResponseDto;
import jeje.work.aeatbe.entity.User;
import jeje.work.aeatbe.exception.UserNotFoundException;
import jeje.work.aeatbe.repository.UserRepository;
import jeje.work.aeatbe.utility.JwtUtil;
import org.springframework.http.MediaType;
Expand All @@ -29,9 +31,13 @@ public KakaoService(KakaoProperties kakaoProperties, UserRepository userReposito
this.jwtUtil = jwtUtil;
}

/**
* @param code 인가코드
* @return KakaoTokenResponsed
*/
public KakaoTokenResponsed getKakaoTokenResponse(String code){
var uri = "https://kauth.kakao.com/oauth/token";
var body = createBody(code);
var body = createLoginBody(code);
var response = restClient.post()
.uri(URI.create(uri))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
Expand All @@ -41,7 +47,7 @@ public KakaoTokenResponsed getKakaoTokenResponse(String code){
return response;
}

private LinkedMultiValueMap<String, String> createBody(String code) {
private LinkedMultiValueMap<String, String> createLoginBody(String code) {
var body = new LinkedMultiValueMap<String, String>();
body.add("grant_type", "authorization_code");
body.add("client_id", kakaoProperties.clientId());
Expand All @@ -50,27 +56,58 @@ private LinkedMultiValueMap<String, String> createBody(String code) {
return body;
}

/**
* 로그인을한다.
* @param accessToken 카카오 엑세스 토큰
* @param refreshToken 카카오 리프레시 토큰
* @return jwt토큰
*/
@Transactional
public String Login(String accessToken, String refreshToken){
public String login(String accessToken, String refreshToken){
var uri = "https://kapi.kakao.com/v2/user/me";
var response = restClient.get()
.uri(URI.create(uri))
.header("Authorization", "Bearer " + accessToken)
.retrieve()
.body(KakaoUserInfo.class);
String userName = response.kakaoAccount().profile().nickname();
String kakaoId = response.id()+"";
Optional<User> user = userRepository.findByKakaoId(kakaoId);
if(user.isEmpty()){
User newUser = User.builder().kakaoId(kakaoId).
accessToken(accessToken).
refreshToken(refreshToken).
build();
User newUser = User.builder().kakaoId(kakaoId)
.userName(userName)
.userImgUrl("")
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
userRepository.save(newUser);
return jwtUtil.createToken(newUser);
}
user.get().kakaoTokenUpdate(accessToken, refreshToken);
return jwtUtil.createToken(user.get());
}

/**
* 카카오 로그아웃 수행
* @param userId
* @return logoutResponseDto
*/
@Transactional
public LogoutResponseDto logout(Long userId){
User user = userRepository.findById(userId)
.orElseThrow(()-> new UserNotFoundException("확인되지 않은 유저 입니다."));

var uri = "https://kapi.kakao.com/v1/user/logout";
var response = restClient.post()
.uri(URI.create(uri))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + user.getAccessToken())
.retrieve()
.body(LogoutResponseDto.class);
user.kakaoTokenUpdate("","");
return response;

}


}
33 changes: 28 additions & 5 deletions src/main/java/jeje/work/aeatbe/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import jeje.work.aeatbe.domian.KakaoProperties;
import jeje.work.aeatbe.domian.KakaoTokenResponsed;
import jeje.work.aeatbe.domian.KakaoUserInfo;
import jeje.work.aeatbe.dto.user.UserInfoResponseDto;
import jeje.work.aeatbe.entity.User;
import jeje.work.aeatbe.exception.UserNotFoundException;
import jeje.work.aeatbe.repository.UserRepository;
import jeje.work.aeatbe.utility.JwtUtil;
import lombok.RequiredArgsConstructor;
Expand All @@ -26,7 +28,11 @@ public class UserService {
private final KakaoService kakaoService;



/**
* 카카오 id로 유저 id(pk)를 반환
* @param kakaoId
* @return Long 유저의 id
*/
public Long getUserId(String kakaoId){
Optional<User> user = userRepository.findByKakaoId(kakaoId);
if(user.isPresent()){
Expand All @@ -35,15 +41,32 @@ public Long getUserId(String kakaoId){
return null;
}

public String createToken(User user) {
return jwtUtil.createToken(user);
}

/**
* 주어진 jwt토큰을 검증하여 이미 있는 유저인지 확인한다.
* @param token
* @return boolean 이미 존재하는 유저인지
*/
public boolean validateToken(String token) {
String kakaoId = jwtUtil.getKakaoId(token);
return userRepository.findByKakaoId(kakaoId).isPresent();
}

/**
* 유저 정보를 반환한다,
* @param userId
* @return UserInfoResponseDto
*/
public UserInfoResponseDto getUserInfo(Long userId){
User user = userRepository.findById(userId)
.orElseThrow(()-> new UserNotFoundException("잘못된 유저입니다."));
return UserInfoResponseDto.builder()
.id(user.getId())
.userName(user.getUserName())
.userImageUrl(user.getUserImgUrl())
.build();

}




Expand Down
4 changes: 3 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ jwt:

kakao:
client_id: 4d3d60da75b24635e63c62f780ab3ea9
redirect_url: http://localhost:8080/api/users/callback
redirect_url: https://kakao-team2-fe.vercel.app/login/redirect
auth_url: https://kauth.kakao.com/oauth/authorize
logout_url: https://kauth.kakao.com/oauth/logout
logout_redirect_url: http://localhost:8080/api/users/logoutWithKakao/callback
Loading