-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[Feat] 카카오 로그인 구현
- Loading branch information
Showing
22 changed files
with
728 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,3 +35,4 @@ out/ | |
|
||
### VS Code ### | ||
.vscode/ | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
src/main/java/com/bloomgroom/domain/user/application/TokenService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package com.bloomgroom.domain.user.application; | ||
|
||
import com.bloomgroom.domain.user.domain.RefreshToken; | ||
import com.bloomgroom.domain.user.domain.User; | ||
import com.bloomgroom.domain.user.domain.repository.RefreshTokenRepository; | ||
import com.bloomgroom.domain.user.domain.repository.UserRepository; | ||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.SignatureAlgorithm; | ||
import io.jsonwebtoken.SignatureException; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
|
||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
import org.springframework.stereotype.Service; | ||
import java.util.Date; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class TokenService { | ||
private final RefreshTokenRepository refreshTokenRepository; | ||
private final UserRepository userRepository; | ||
|
||
@Value("${jwt.secret-key}") | ||
private String secretKey; | ||
|
||
// AccessToken 생성 | ||
public String createAccessToken(User user) { //AccessToken을 JWT형식으로 생성함 | ||
return Jwts.builder() | ||
.setSubject(user.getUserId().toString()) | ||
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) //1시간 | ||
.signWith(SignatureAlgorithm.HS256, secretKey) | ||
.compact(); | ||
} | ||
|
||
// RefreshToken 생성 | ||
public String createRefreshToken(User user) { | ||
return Jwts.builder() | ||
.setSubject(user.getUserId().toString()) | ||
.setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 3600000)) //1주일 | ||
.signWith(SignatureAlgorithm.HS256, secretKey) | ||
.compact(); | ||
} | ||
|
||
// RefreshToken 저장 | ||
public void saveRefreshToken(User user, String refreshToken) { | ||
|
||
RefreshToken token = new RefreshToken(); | ||
|
||
token.setRefreshToken(refreshToken); | ||
token.setUser(user); | ||
refreshTokenRepository.save(token); | ||
} | ||
|
||
//RefreshToken 이용하여 AccessToken 재발급 | ||
public String renewAccessToken(String refreshToken) { | ||
|
||
RefreshToken token = refreshTokenRepository.findByRefreshToken(refreshToken).orElse(null); | ||
|
||
if (token == null) { | ||
return null; | ||
} | ||
User user = token.getUser(); | ||
return createAccessToken(user); | ||
} | ||
|
||
// RefreshToken 조회 | ||
public String getRefreshToken(User user) { | ||
RefreshToken refreshToken = refreshTokenRepository.findByUser(user); | ||
|
||
if (refreshToken != null) { | ||
return refreshToken.getRefreshToken(); | ||
} | ||
return null; | ||
} | ||
|
||
} |
110 changes: 110 additions & 0 deletions
110
src/main/java/com/bloomgroom/domain/user/application/UserService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package com.bloomgroom.domain.user.application; | ||
|
||
import com.bloomgroom.domain.user.domain.User; | ||
import com.bloomgroom.domain.user.domain.repository.RefreshTokenRepository; | ||
import com.bloomgroom.domain.user.domain.repository.UserRepository; | ||
import com.bloomgroom.domain.user.dto.response.KakaoTokenRes; | ||
import com.nimbusds.jose.shaded.gson.JsonObject; | ||
import com.nimbusds.jose.shaded.gson.JsonParser; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.*; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.util.MultiValueMap; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.util.Optional; | ||
|
||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class UserService { | ||
|
||
private final UserRepository userRepository; | ||
private final RefreshTokenRepository refreshTokenRepository; | ||
private final RestTemplate restTemplate = new RestTemplate(); | ||
|
||
|
||
|
||
@Value("${spring.security.oauth2.client.registration.kakao.client-id}") | ||
private String kakaoClientId; | ||
|
||
@Value("${spring.security.oauth2.client.registration.kakao.redirect-uri}") | ||
private String redirectUri; | ||
|
||
@Value("${spring.security.oauth2.client.provider.kakao.token-uri}") | ||
private String kakaoTokenUrl; | ||
|
||
@Value("${spring.security.oauth2.client.provider.kakao.user-info-uri}") | ||
private String kakaoUserInfoUrl; | ||
|
||
@Value("${spring.security.oauth2.client.registration.kakao.client-secret}") | ||
private String kakaoClientSecret; | ||
|
||
@Value("${kakao.admin-key}") | ||
private String adminKey; | ||
|
||
// code로 accessToken 받기 | ||
public String getKakaoAccessToken(String code) { | ||
|
||
HttpHeaders headers = new HttpHeaders(); | ||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); | ||
|
||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); | ||
params.add("grant_type", "authorization_code"); | ||
params.add("client_id", kakaoClientId); | ||
params.add("redirect_uri", redirectUri); | ||
params.add("client_secret", kakaoClientSecret); | ||
params.add("code", code); | ||
|
||
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers); | ||
ResponseEntity<KakaoTokenRes> response = restTemplate.exchange( | ||
kakaoTokenUrl, HttpMethod.POST, request, KakaoTokenRes.class); | ||
|
||
return response.getBody().getAccessToken(); | ||
} | ||
|
||
|
||
// 카카오에서 사용자 정보 가져오기 | ||
public User getKakaoUser(String accessToken) { | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.set("Authorization", "Bearer " + accessToken); | ||
|
||
HttpEntity<Void> request = new HttpEntity<>(headers); | ||
ResponseEntity<String> response = restTemplate.exchange( | ||
kakaoUserInfoUrl, HttpMethod.GET, request, String.class); | ||
|
||
// JSON 응답 본문에서 직접 정보 추출 | ||
String responseBody = response.getBody(); | ||
if (responseBody != null) { | ||
|
||
JsonObject jsonObject = JsonParser.parseString(responseBody).getAsJsonObject(); | ||
JsonObject kakaoAccount = jsonObject.getAsJsonObject("kakao_account"); | ||
JsonObject properties = jsonObject.getAsJsonObject("properties"); | ||
|
||
|
||
String email = kakaoAccount.get("email").getAsString(); | ||
|
||
User user = new User(); | ||
user.setEmail(email); | ||
return user; | ||
} | ||
|
||
throw new RuntimeException("유저의 정보가 없습니다."); | ||
} | ||
|
||
|
||
// 회원가입 (카카오에서 받은 정보를 DB에 저장) | ||
public User signup(User user) { | ||
return userRepository.save(user); | ||
} | ||
|
||
public User findByEmail(String email) { | ||
return userRepository.findByEmail(email).orElse(null); | ||
} | ||
|
||
public Optional<User> findUserById(Long userId) { | ||
return userRepository.findById(userId); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
src/main/java/com/bloomgroom/domain/user/domain/RefreshToken.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.bloomgroom.domain.user.domain; | ||
|
||
import jakarta.persistence.*; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.Setter; | ||
|
||
@Entity | ||
@Getter | ||
@Setter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class RefreshToken { | ||
|
||
//refreshToken을 가지고 accessToken을 발급해주는 거임 | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
|
||
@Column(nullable = false, unique = true, length = 255) | ||
private String refreshToken; | ||
|
||
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) | ||
@JoinColumn(name = "user_id") | ||
private User user; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.bloomgroom.domain.user.domain; | ||
|
||
import jakarta.persistence.*; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.Setter; | ||
|
||
@Entity | ||
@Getter | ||
@Setter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class User { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Column(name = "user_id") | ||
private Long userId; | ||
|
||
@Column(name = "email", nullable = false, unique = true, length = 255) | ||
private String email; | ||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/bloomgroom/domain/user/domain/repository/RefreshTokenRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.bloomgroom.domain.user.domain.repository; | ||
|
||
import com.bloomgroom.domain.user.domain.RefreshToken; | ||
import com.bloomgroom.domain.user.domain.User; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.Optional; | ||
|
||
@Repository | ||
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> { | ||
Optional<RefreshToken> findByRefreshToken(String refreshToken); | ||
RefreshToken findByUser(User user); | ||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/com/bloomgroom/domain/user/domain/repository/UserRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.bloomgroom.domain.user.domain.repository; | ||
|
||
import com.bloomgroom.domain.user.domain.User; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.Optional; | ||
@Repository | ||
public interface UserRepository extends JpaRepository<User, Long> { | ||
Optional<User> findByEmail(String email); | ||
Optional<User> findByUserId(Long userId); | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/com/bloomgroom/domain/user/dto/request/KakaoTokenReq.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.bloomgroom.domain.user.dto.request; | ||
|
||
import lombok.Data; | ||
|
||
@Data | ||
public class KakaoTokenReq { | ||
private String refreshToken; | ||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/com/bloomgroom/domain/user/dto/request/TokenReq.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.bloomgroom.domain.user.dto.request; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Data | ||
public class TokenReq { | ||
|
||
private String accessToken; | ||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/com/bloomgroom/domain/user/dto/response/BasicResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.bloomgroom.domain.user.dto.response; | ||
|
||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@Data | ||
@NoArgsConstructor | ||
public class BasicResponse <T>{ //API response 깃에 올라오면 수정봐야함 | ||
|
||
private String message; | ||
private int statusCode; | ||
private T data; | ||
private HttpStatus status; | ||
|
||
// 모든 필드를 포함하는 생성자 추가 | ||
public BasicResponse(String message, int statusCode, T data, HttpStatus status) { | ||
this.message = message; | ||
this.statusCode = statusCode; | ||
this.data = data; | ||
this.status = status; | ||
} | ||
|
||
public static <T> BasicResponse<T> ofSuccess(T data) { | ||
return new BasicResponse<>("SUCCESS", HttpStatus.OK.value(), data, HttpStatus.OK); | ||
} | ||
|
||
public static <T> BasicResponse<T> ofFailure(String message, HttpStatus status) { | ||
return new BasicResponse<>(message, status.value(), null, status); | ||
} | ||
|
||
public static <T> BasicResponse<T> ofSuccess(T data, String message) { | ||
return new BasicResponse<>(message, HttpStatus.OK.value(), data, HttpStatus.OK); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/bloomgroom/domain/user/dto/response/KakaoLoginRes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.bloomgroom.domain.user.dto.response; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Data | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class KakaoLoginRes { | ||
|
||
private String accessToken; // JWT 액세스 토큰 | ||
private String refreshToken; // 리프레시 토큰 | ||
} |
Oops, something went wrong.