Skip to content

Commit

Permalink
Feat [#20] Amazon S3 설정 & 시큐리티 설정 클래스 수정 (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
geniusYoo authored Jul 4, 2024
2 parents cc0ec38 + 7b30e4f commit 39181ed
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 3 deletions.
4 changes: 4 additions & 0 deletions jaksim/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ dependencies {
// implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'com.corundumstudio.socketio:netty-socketio:2.0.9'

// Multipart File - Amazon S3
implementation("software.amazon.awssdk:bom:2.21.0")
implementation("software.amazon.awssdk:s3:2.21.0")

// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public class SecurityConfig {
private final CustomAccessDeniedHandler customAccessDeniedHandler;

private static final String[] AUTH_WHITE_LIST = {ACTIVATE_PROFILE_URL,
"/login/**", "/api/v1/auth/**",
"/api/v1/user/signin/**", "/api/v1/auth/**",
"/api/v1/user/reissue/**", "/socket.io/**",
"/swagger-ui/**", "/swagger-resources/**"};

@Bean
Expand Down
81 changes: 81 additions & 0 deletions jaksim/src/main/java/org/sopt/jaksim/global/common/S3Service.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.sopt.jaksim.global.common;

import org.sopt.jaksim.global.config.AwsConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Component
public class S3Service {

private final String bucketName;
private final AwsConfig awsConfig;
private static final List<String> IMAGE_EXTENSIONS = Arrays.asList("image/jpeg", "image/png", "image/jpg", "image/webp");


public S3Service(@Value("${aws-property.s3-bucket-name}") final String bucketName, AwsConfig awsConfig) {
this.bucketName = bucketName;
this.awsConfig = awsConfig;
}


public String uploadImage(String directoryPath, MultipartFile image) throws IOException {
final String key = directoryPath + generateImageFileName();
final S3Client s3Client = awsConfig.getS3Client();

validateExtension(image);
validateFileSize(image);

PutObjectRequest request = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.contentType(image.getContentType())
.contentDisposition("inline")
.build();

RequestBody requestBody = RequestBody.fromBytes(image.getBytes());
s3Client.putObject(request, requestBody);
return key;
}

public void deleteImage(String key) throws IOException {
final S3Client s3Client = awsConfig.getS3Client();

s3Client.deleteObject((DeleteObjectRequest.Builder builder) ->
builder.bucket(bucketName)
.key(key)
.build()
);
}


private String generateImageFileName() {
return UUID.randomUUID() + ".jpg";
}


private void validateExtension(MultipartFile image) {
String contentType = image.getContentType();
if (!IMAGE_EXTENSIONS.contains(contentType)) {
throw new RuntimeException("이미지 확장자는 jpg, png, webp만 가능합니다.");
}
}

private static final Long MAX_FILE_SIZE = 5 * 1024 * 1024L;

private void validateFileSize(MultipartFile image) {
if (image.getSize() > MAX_FILE_SIZE) {
throw new RuntimeException("이미지 사이즈는 5MB를 넘을 수 없습니다.");
}
}

}
48 changes: 48 additions & 0 deletions jaksim/src/main/java/org/sopt/jaksim/global/config/AwsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.sopt.jaksim.global.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

@Configuration
public class AwsConfig {

private static final String AWS_ACCESS_KEY_ID = "aws.accessKeyId";
private static final String AWS_SECRET_ACCESS_KEY = "aws.secretAccessKey";

private final String accessKey;
private final String secretKey;
private final String regionString;

public AwsConfig(@Value("${aws-property.access-key}") final String accessKey,
@Value("${aws-property.secret-key}") final String secretKey,
@Value("${aws-property.aws-region}") final String regionString) {
this.accessKey = accessKey;
this.secretKey = secretKey;
this.regionString = regionString;
}


@Bean
public SystemPropertyCredentialsProvider systemPropertyCredentialsProvider() {
System.setProperty(AWS_ACCESS_KEY_ID, accessKey);
System.setProperty(AWS_SECRET_ACCESS_KEY, secretKey);
return SystemPropertyCredentialsProvider.create();
}

@Bean
public Region getRegion() {
return Region.of(regionString);
}

@Bean
public S3Client getS3Client() {
return S3Client.builder()
.region(getRegion())
.credentialsProvider(systemPropertyCredentialsProvider())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ public ResponseEntity<BaseResponse<?>> signup(@RequestBody final UserSignUpReque
}

@Override
@PostMapping("/user/signin")
@PostMapping("/users/signin")
public ResponseEntity<BaseResponse<?>> signin(@RequestHeader(AUTHORIZATION) final String accessToken) {
final UserSignInResponse response = userFacade.signin();
return ApiResponseUtil.success(SuccessMessage.USER_SIGN_IN_SUCCESS, response);
}

@PostMapping("/reissue")
@PostMapping("/users/reissue")
public ResponseEntity<BaseResponse<?>> reissue(@RequestHeader(AUTHORIZATION) final String refreshToken,
@RequestBody final UserReissueRequest userReissueRequest) {
UserSignInResponse response = userFacade.reissue(refreshToken, userReissueRequest);
Expand Down

0 comments on commit 39181ed

Please sign in to comment.