Skip to content

Commit

Permalink
제발제발제발제제발ㅈㅂ
Browse files Browse the repository at this point in the history
  • Loading branch information
zzzzseong committed Jan 15, 2024
1 parent 7dd93e1 commit 0e23941
Show file tree
Hide file tree
Showing 66 changed files with 3,480 additions and 0 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: 'sports_echo_ci'
on:
push:
branches: [ "feature/*", "hotfix" ]
pull_request:
branches: [ "dev1" ]
permissions:
contents: read
pull-requests: read
jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Git Checkout
uses: actions/checkout@v4

# .properties file 생성
- name: application.properties 생성
run: |
touch ./src/main/resources/application-prod.yml
touch ./src/main/resources/application-test.yml
echo "${{ secrets.PROD_YML }}" > ./application-prod.yml
echo "${{ secrets.TEST_PROD_YML }}" > ./application-test.yml
- name: Java Setup
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'oracle'
cache: gradle

- name: Build with Gradle
env:
SPRING_PROFILES_ACTIVE: test
run: |
./gradlew clean build
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v1
if: ${{ always() }}
with:
files: build/test-results/**/*.xml

- name: Upload Jacoco Report
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: jacoco-report
path: build/reports/jacoco/test/html
158 changes: 158 additions & 0 deletions src/main/java/com/sportsecho/common/oauth/OAuthUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.sportsecho.common.oauth;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sportsecho.common.oauth.exception.OAuthErrorCode;
import com.sportsecho.global.exception.GlobalException;
import com.sportsecho.member.entity.Member;
import com.sportsecho.member.entity.MemberRole;
import com.sportsecho.member.repository.MemberRepository;
import java.net.URI;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

/**
* kakaoOAuthDocs: https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#request-token
* naverOAuthDocs: https://developers.naver.com/docs/login/devguide/devguide.md
* googleOAuthDocs: https://developers.google.com/identity/protocols/oauth2/web-server?hl=ko
* */

@Service
@Slf4j(topic = "OAUthUtil")
@RequiredArgsConstructor
public class OAuthUtil {

@Value("${oauth.api.key.kakao}")
private String kakaoApiKey;

@Value("${oauth.api.key.naver}")
private String naverApiKey;

@Value("${oauth.api.secret.naver}")
private String naverApiSecret;

@Value("${oauth.api.key.google}")
private String googleApiKey;

@Value("${oauth.api.secret.google}")
private String googleApiSecret;

private final MemberRepository memberRepository;

private final RestTemplate restTemplate;
private final PasswordEncoder passwordEncoder;

public JsonNode getToken(URI uri, SocialType socialType, String code) {
try {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(generateBody(socialType, code));

// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);

// HTTP 응답 (JSON) -> 액세스 토큰 파싱
return new ObjectMapper().readTree(response.getBody());
} catch (JsonProcessingException e) {
throw new GlobalException(OAuthErrorCode.ILLEGAL_REQUEST);
}
}

public JsonNode getMemberInfo(URI uri, String accessToken) {
try {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(new LinkedMultiValueMap<>());

//HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);

return new ObjectMapper().readTree(response.getBody());
} catch(JsonProcessingException e) {
throw new GlobalException(OAuthErrorCode.ILLEGAL_REQUEST);
}
}

public Member registerSocialMemberIfNeeded(Long socialId, String memberName, String email, SocialType socialType) {
Member socialMember = memberRepository.findBySocialIdAndSocialType(socialId, socialType).orElse(null);

if (socialMember == null) {
// 소셜 사용자 email과 동일한 email 가진 회원이 있는지 확인
Member sameEmailMember = memberRepository.findByEmail(email).orElse(null);

if (sameEmailMember != null) {
socialMember = sameEmailMember;
} else {
String encodedPassword = passwordEncoder.encode(UUID.randomUUID().toString());

socialMember = Member.builder()
.memberName(memberName)
.email(email)
.password(encodedPassword)
.role(MemberRole.CUSTOMER)
.build();
}

//socialId update 및 저장
socialMember = socialMember.updateSocialIdAndType(socialId, socialType);
memberRepository.save(socialMember);
}

return socialMember;
}

private MultiValueMap<String, String> generateBody(SocialType socialType, String code) {
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();

if(SocialType.KAKAO.equals(socialType)) {
body.add("grant_type", "authorization_code");
body.add("client_id", kakaoApiKey);
body.add("redirect_uri", "http://localhost:8080/api/members/kakao/callback");
body.add("code", code);
}
if(SocialType.NAVER.equals(socialType)) {
body.add("grant_type", "authorization_code");
body.add("client_id", naverApiKey);
body.add("client_secret", naverApiSecret);
body.add("code", code);
body.add("state", "9kgsGTfH4j7IyAkg");
}
if(SocialType.GOOGLE.equals(socialType)) {
body.add("grant_type", "authorization_code");
body.add("client_id", googleApiKey);
body.add("client_secret", googleApiSecret);
body.add("code", code);
body.add("redirect_uri", "http://localhost:8080/api/members/google/callback");
}

return body;
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/sportsecho/common/oauth/SocialType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.sportsecho.common.oauth;

public enum SocialType {
KAKAO, NAVER, GOOGLE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.sportsecho.common.oauth.exception;

import com.sportsecho.global.exception.BaseErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum OAuthErrorCode implements BaseErrorCode {

ILLEGAL_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 OAuth 로그인 요청입니다."),
;

private final HttpStatus status;
private final String msg;
}
32 changes: 32 additions & 0 deletions src/main/java/com/sportsecho/global/util/s3/AWSConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.sportsecho.global.util.s3;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AWSConfig {

@Value("${cloud.aws.credentials.accessKey}")
private String iamAccessKey; // IAM Access Key

@Value("${cloud.aws.credentials.secretKey}")
private String iamSecretKey; // IAM Secret Key

private String region = "ap-northeast-2"; // Bucket Region (서울)

@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(iamAccessKey, iamSecretKey);
return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.build();
}


}
51 changes: 51 additions & 0 deletions src/main/java/com/sportsecho/global/util/s3/S3Uploader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.sportsecho.global.util.s3;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.PutObjectRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RequiredArgsConstructor
@Service
public class S3Uploader {

private final AmazonS3Client amazonS3Client;

@Value("${cloud.aws.s3.bucket}")
private String bucket;

@Value("${cloud.aws.region.static}")
private String region;

public String upload(MultipartFile file, String filename) {
File fileObj = converMultiPartFileToFile(file);
amazonS3Client.putObject(new PutObjectRequest(bucket, filename, fileObj));
fileObj.delete();

String fileurl = amazonS3Client.getUrl(bucket, filename).toString();

return fileurl;
}

private File converMultiPartFileToFile(MultipartFile file) {
File convertedFile = new File(Objects.requireNonNull(file.getOriginalFilename()));
try (FileOutputStream fileOutputStream = new FileOutputStream(convertedFile)) {
fileOutputStream.write(file.getBytes());
} catch (IOException e) {
log.error("파일 변환 실패 : ", e);
}
return convertedFile;
}

public void deleteFile(String filename) {
amazonS3Client.deleteObject(bucket, filename);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.sportsecho.global.util.s3.controller;

import com.sportsecho.global.util.s3.service.FileUploadService;
import com.sportsecho.member.entity.MemberDetailsImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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;
import org.springframework.web.multipart.MultipartFile;

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

private final FileUploadService fileUploadService;

@PostMapping("/image")
public String uploadProductImage(
@RequestParam(value = "file")MultipartFile file,
@RequestParam(value = "identifier") String identifier,
@AuthenticationPrincipal MemberDetailsImpl memberDetails
) {
return fileUploadService.uploadFile(memberDetails.getMember(), file, identifier);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.sportsecho.global.util.s3.service;

import com.sportsecho.global.exception.GlobalException;
import com.sportsecho.global.util.s3.S3Uploader;
import com.sportsecho.member.entity.Member;
import com.sportsecho.member.entity.MemberRole;
import com.sportsecho.product.exception.ProductErrorCode;
import com.sportsecho.product.repository.ProductRepository;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
@RequiredArgsConstructor
public class FileUploadService {

private final S3Uploader s3Uploader;

public String uploadFile(Member member, MultipartFile file, String identifier) {

// if (member.getRole().equals(MemberRole.CUSTOMER)) {
// throw new GlobalException(ProductErrorCode.NO_AUTHORIZATION);
// }

UUID uuid = UUID.randomUUID();
String fileName = identifier + uuid;

return s3Uploader.upload(file, fileName);
}
}
Loading

0 comments on commit 0e23941

Please sign in to comment.