Skip to content

Commit

Permalink
Merge pull request #14 from Leets-Official/11-feature-깃토큰-db-암호화하여-저장
Browse files Browse the repository at this point in the history
11 feature: 깃토큰 db 암호화하여 저장
  • Loading branch information
leejuae authored Jul 23, 2024
2 parents 73f710d + b8b9626 commit 51401ee
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.leets.commitatobe.domain.login.usecase.LoginQueryService;
import com.leets.commitatobe.domain.user.domain.User;
import com.leets.commitatobe.domain.user.domain.repository.UserRepository;
import com.leets.commitatobe.domain.user.usecase.UserQueryService;
import com.leets.commitatobe.global.exception.ApiException;
import com.leets.commitatobe.global.response.code.status.ErrorStatus;
import com.leets.commitatobe.global.response.code.status.SuccessStatus;
Expand All @@ -30,6 +31,7 @@ public class FetchCommits {
private final GitHubService gitHubService; // GitHub API 통신
private final LoginCommandServiceImpl loginCommandService;
private final LoginQueryService loginQueryService;
private final UserQueryService userQueryService;

public CommitResponse execute(HttpServletRequest request) {
String gitHubId = loginQueryService.getGitHubId(request);
Expand All @@ -47,8 +49,12 @@ public CommitResponse execute(HttpServletRequest request) {
}

try {
// Github API Access Token 저장
gitHubService.updateToken(loginCommandService.gitHubLogin(gitHubId));
// 기존: Github API Access Token 저장
// gitHubService.updateToken(loginCommandService.gitHubLogin(gitHubId));

//변경: DB에서 엑세스 토큰 불러오도록 방식 변경
String gitHubaccessToken = userQueryService.getUserGitHubAccessToken(gitHubId);
gitHubService.updateToken(gitHubaccessToken);

List<String> repos = gitHubService.fetchRepos(gitHubId);
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.gson.JsonParser;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
Expand All @@ -28,6 +29,9 @@ public class GitHubService {
private final String GITHUB_API_URL = "https://api.github.com";
private String AUTH_TOKEN;
private final Map<LocalDateTime, Integer> commitsByDate = new HashMap<>();
@Value("${domain-uri}")
private String DOMAIN_URI;
private String loginURL = DOMAIN_URI + "/login/github";

// GitHub repository 이름 저장
public List<String> fetchRepos(String gitHubUsername) throws IOException {
Expand Down Expand Up @@ -125,6 +129,17 @@ private HttpURLConnection getConnection(URL url) throws IOException {
connection.setRequestMethod("GET");
connection.setRequestProperty("Authorization", "token " + AUTH_TOKEN);
connection.setRequestProperty("Accept", "application/vnd.github.v3+json");

// AUTH_TOKEN 유효한지 확인
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
// AUTH_TOKEN이 유효하지 않으면 리다이렉트
URL loginUrl = new URL(loginURL);
connection = (HttpURLConnection) loginUrl.openConnection();
connection.setInstanceFollowRedirects(true);
connection.connect();
}

return connection;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.leets.commitatobe.domain.login.presentation.dto.JwtResponse;
import com.leets.commitatobe.domain.login.usecase.LoginCommandService;
import com.leets.commitatobe.domain.login.usecase.LoginQueryService;
import com.leets.commitatobe.domain.user.usecase.UserQueryService;
import com.leets.commitatobe.global.config.CustomOAuth2UserService;
import com.leets.commitatobe.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -25,6 +26,7 @@ public class LoginController {

private final LoginCommandService loginCommandService;
private final LoginQueryService loginQueryService;
private final UserQueryService userQueryService;

private final CustomOAuth2UserService customOAuth2UserService;

Expand All @@ -50,9 +52,9 @@ public void redirectToGitHub(HttpServletResponse response) {
@GetMapping("/callback")
public ApiResponse<JwtResponse> githubCallback(@RequestParam("code") String code, HttpServletResponse response) {
// GitHub에서 받은 인가 코드로 액세스 토큰 요청
String accessToken = loginCommandService.gitHubLogin(code);
String gitHubAccessToken = loginCommandService.gitHubLogin(code);
// 액세스 토큰을 이용하여 JWT 생성
JwtResponse jwt = customOAuth2UserService.generateJwt(accessToken);
JwtResponse jwt = customOAuth2UserService.generateJwt(gitHubAccessToken);

// 액세스 토큰을 헤더에 설정
response.setHeader("Authentication", "Bearer " + jwt.accessToken());
Expand All @@ -68,6 +70,8 @@ public ApiResponse<JwtResponse> githubCallback(@RequestParam("code") String code
@GetMapping("/test")
public ApiResponse<GitHubDto> test(HttpServletRequest request) {
GitHubDto user = loginQueryService.getGitHubUser(request);
String gitHubAccessToken = userQueryService.getUserGitHubAccessToken(user.userId());
log.info("깃허브 엑세스 토큰: {}", gitHubAccessToken);
return ApiResponse.onSuccess(user);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ public interface LoginCommandService {
// 응답에 쿠키 설정 함수
void setRefreshTokenCookie(HttpServletResponse response, String refreshToken);

String encrypt(String token);

String decrypt(String token);

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package com.leets.commitatobe.domain.login.usecase;

import static ch.qos.logback.core.encoder.ByteArrayUtil.hexStringToByteArray;
import static com.leets.commitatobe.global.response.code.status.ErrorStatus._DECRYPT_ERROR;
import static com.leets.commitatobe.global.response.code.status.ErrorStatus._ENCRYPT_ERROR;
import static com.leets.commitatobe.global.response.code.status.ErrorStatus._GITHUB_JSON_PARSING_ERROR;
import static com.leets.commitatobe.global.response.code.status.ErrorStatus._GITHUB_TOKEN_GENERATION_ERROR;
import static com.leets.commitatobe.global.response.code.status.ErrorStatus._REDIRECT_ERROR;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.leets.commitatobe.domain.user.domain.repository.UserRepository;
import com.leets.commitatobe.global.exception.ApiException;
import com.leets.commitatobe.global.utils.JwtProvider;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -37,11 +43,16 @@ public class LoginCommandServiceImpl implements LoginCommandService {
@Value("${spring.security.oauth2.client.registration.github.redirect-uri}")
private String redirectUri;

private final JwtProvider jwtProvider;
// 깃허브 엑세스 토큰 및 리프레쉬 토큰 암호화를 위한 변수
@Value("${jwt.aes-secret}")
private String aesSecret;

private final UserRepository userRepository;
@Value("${jwt.iv-secret}")
private String ivSecret;

// 암호화 알고리즘
private String alg = "AES/CBC/PKCS5Padding";

// 예찬님! 이 함수 사용해서 accessToken 가져오심 됩니다~!
public String gitHubLogin(String authCode) {
RestTemplate restTemplate = new RestTemplate();

Expand Down Expand Up @@ -110,6 +121,42 @@ public void setRefreshTokenCookie(HttpServletResponse response, String refreshTo
response.addCookie(refreshTokenCookie);
}

@Override
public String encrypt(String token) {
byte[] encrypted = null;
try{
Cipher cipher = Cipher.getInstance(alg);
SecretKeySpec keySpec = new SecretKeySpec(hexStringToByteArray(aesSecret), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(hexStringToByteArray(ivSecret));
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);

encrypted = cipher.doFinal(token.getBytes(StandardCharsets.UTF_8));
} catch (Exception e){
throw new ApiException(_ENCRYPT_ERROR);
}


return Base64.getEncoder().encodeToString(encrypted);
}

@Override
public String decrypt(String token) {
byte[] decrypted = null;

try{
Cipher cipher = Cipher.getInstance(alg);
SecretKeySpec keySpec = new SecretKeySpec(hexStringToByteArray(aesSecret), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(hexStringToByteArray(ivSecret));
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);

byte[] decodedBytes = Base64.getDecoder().decode(token);
decrypted = cipher.doFinal(decodedBytes);
} catch (Exception e){
throw new ApiException(_DECRYPT_ERROR);
}

return new String(decrypted, StandardCharsets.UTF_8);
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public class User extends BaseTimeEntity {
@Column(name = "user_id")
private UUID id;

@Column
private String gitHubAccessToken;

@Column
private String refreshToken;

@Column(nullable = true)
private String username;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package com.leets.commitatobe.domain.user.presentation.dto.response;

public record UserResponse() {
public record UserResponse(
RefreshTokenResponse refreshTokenResponse,
GitHubAccessTokenResponse gitHubAccessTokenResponse
) {
public record RefreshTokenResponse(
String refreshToken
) {}

public record GitHubAccessTokenResponse(
String gitHubAccessToken
) {}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.leets.commitatobe.domain.user.usecase;


import jakarta.servlet.http.HttpServletRequest;

public interface UserQueryService {

String getUserGitHubAccessToken(String githubId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package com.leets.commitatobe.domain.user.usecase;

import static com.leets.commitatobe.global.response.code.status.ErrorStatus._USER_NOT_FOUND;

import com.leets.commitatobe.domain.login.usecase.LoginCommandService;
import com.leets.commitatobe.domain.user.domain.User;
import com.leets.commitatobe.domain.user.domain.repository.UserRepository;
import com.leets.commitatobe.global.exception.ApiException;
import com.leets.commitatobe.global.utils.JwtProvider;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -9,5 +17,24 @@
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserQueryServiceImpl implements UserQueryService {
private final JwtProvider jwtProvider;

private final UserRepository userRepository;

private final LoginCommandService loginCommandService;

@Override
public String getUserGitHubAccessToken(String githubId) {
Optional<User> user = userRepository.findByGithubId(githubId);

if(user == null){
throw new ApiException(_USER_NOT_FOUND);
}

String gitHubAccessToken = user.get().getGitHubAccessToken();

String decodedGitHubAccessToken = loginCommandService.decrypt(gitHubAccessToken);

return decodedGitHubAccessToken;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.leets.commitatobe.global.config;

import com.leets.commitatobe.domain.login.presentation.dto.JwtResponse;
import com.leets.commitatobe.domain.login.usecase.LoginCommandService;
import com.leets.commitatobe.domain.user.domain.User;
import com.leets.commitatobe.domain.user.domain.repository.UserRepository;
import com.leets.commitatobe.global.utils.JwtProvider;
Expand Down Expand Up @@ -37,8 +38,11 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private JwtProvider jwtProvider;
private final UserRepository userRepository;

@Autowired
private LoginCommandService loginCommandService;

// Jwt 생성 메인 함수
public JwtResponse generateJwt (String authCode){
public JwtResponse generateJwt (String gitHubAccessToken){
// 사용자 정보를 가져와 jwt 생성
// ClientRegistration 객체 생성
ClientRegistration clientRegistration = ClientRegistration.withRegistrationId("github")
Expand All @@ -55,11 +59,11 @@ public JwtResponse generateJwt (String authCode){
// OAuth2UserRequest 생성
OAuth2UserRequest userRequest = new OAuth2UserRequest(
clientRegistration,
new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, authCode, Instant.now(), Instant.now().plusSeconds(3600))
new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, gitHubAccessToken, Instant.now(), Instant.now().plusSeconds(3600))
);

// 사용자 정보와 JWT 토큰 생성
JwtResponse jwt = loadUserAndJwt(userRequest);
JwtResponse jwt = loadUserAndJwt(userRequest, gitHubAccessToken);

return jwt;
}
Expand All @@ -77,29 +81,32 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
}

// 사용자 정보를 바탕으로 jwt 생성
public JwtResponse loadUserAndJwt(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
public JwtResponse loadUserAndJwt(OAuth2UserRequest userRequest, String gitHubAccessToken) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = loadUser(userRequest);
String githubId = oAuth2User.getAttribute("login");

JwtResponse jwt = jwtProvider.generateTokenDto(githubId);

// GitHub에서 받은 사용자 정보를 바탕으로 Member 엔티티를 조회하거나 새로 생성
userRepository.findByGithubId(githubId)
.orElseGet(() -> createNewUser(oAuth2User, jwt.refreshToken()));
.orElseGet(() -> createNewUser(oAuth2User, jwt.refreshToken(), gitHubAccessToken));

return jwt;
}

// User 생성 메서드
public User createNewUser(OAuth2User oAuth2User, String refreshToken) {
public User createNewUser(OAuth2User oAuth2User, String refreshToken, String gitHubAccessToken) {
String githubId = oAuth2User.getAttribute("login");
String username = oAuth2User.getAttribute("name");
String profileImage = oAuth2User.getAttribute("avatar_url");

String encryptedGitHubAccessToken = loginCommandService.encrypt(gitHubAccessToken);

User user = User.builder()
.githubId(githubId)
.username(username)
.profileImage(profileImage)
.gitHubAccessToken(encryptedGitHubAccessToken)
.build();

User savedUser = userRepository.save(user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,16 @@ public enum ErrorStatus implements BaseErrorCode {
_COMMIT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMIT_001", "커밋이 없습니다."),

// GitHub API 관련
_GIT_URL_INCORRECT(HttpStatus.BAD_REQUEST, "GIT_001", "잘못된 URL로 요청하고 있습니다.");
_GIT_URL_INCORRECT(HttpStatus.BAD_REQUEST, "GIT_001", "잘못된 URL로 요청하고 있습니다."),

// 인코딩 오류
_ENCRYPT_ERROR(HttpStatus.BAD_REQUEST, "ENCRYPT_001", "토큰 인코딩 과정에서 오류가 발생했습니다."),

// 디코딩 오류
_DECRYPT_ERROR(HttpStatus.BAD_REQUEST, "DECRYPT_001", "토큰 디코딩 과정에서 오류가 발생했습니다."),

// 유저 관련
_USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER_001", "DB에서 유저를 불러오는 과정에서 오류가 발생했습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down

0 comments on commit 51401ee

Please sign in to comment.