Skip to content

Commit 39181ed

Browse files
authored
Feat [#20] Amazon S3 설정 & 시큐리티 설정 클래스 수정 (#21)
2 parents cc0ec38 + 7b30e4f commit 39181ed

File tree

5 files changed

+137
-3
lines changed

5 files changed

+137
-3
lines changed

jaksim/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ dependencies {
4848
// implementation 'org.springframework.boot:spring-boot-starter-websocket'
4949
implementation 'com.corundumstudio.socketio:netty-socketio:2.0.9'
5050

51+
// Multipart File - Amazon S3
52+
implementation("software.amazon.awssdk:bom:2.21.0")
53+
implementation("software.amazon.awssdk:s3:2.21.0")
54+
5155
// test
5256
testImplementation 'org.springframework.boot:spring-boot-starter-test'
5357
testImplementation 'org.springframework.security:spring-security-test'

jaksim/src/main/java/org/sopt/jaksim/auth/SecurityConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public class SecurityConfig {
2929
private final CustomAccessDeniedHandler customAccessDeniedHandler;
3030

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

3536
@Bean
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package org.sopt.jaksim.global.common;
2+
3+
import org.sopt.jaksim.global.config.AwsConfig;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.multipart.MultipartFile;
7+
import software.amazon.awssdk.core.sync.RequestBody;
8+
import software.amazon.awssdk.services.s3.S3Client;
9+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
10+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
11+
12+
import java.io.IOException;
13+
import java.util.Arrays;
14+
import java.util.List;
15+
import java.util.UUID;
16+
17+
@Component
18+
public class S3Service {
19+
20+
private final String bucketName;
21+
private final AwsConfig awsConfig;
22+
private static final List<String> IMAGE_EXTENSIONS = Arrays.asList("image/jpeg", "image/png", "image/jpg", "image/webp");
23+
24+
25+
public S3Service(@Value("${aws-property.s3-bucket-name}") final String bucketName, AwsConfig awsConfig) {
26+
this.bucketName = bucketName;
27+
this.awsConfig = awsConfig;
28+
}
29+
30+
31+
public String uploadImage(String directoryPath, MultipartFile image) throws IOException {
32+
final String key = directoryPath + generateImageFileName();
33+
final S3Client s3Client = awsConfig.getS3Client();
34+
35+
validateExtension(image);
36+
validateFileSize(image);
37+
38+
PutObjectRequest request = PutObjectRequest.builder()
39+
.bucket(bucketName)
40+
.key(key)
41+
.contentType(image.getContentType())
42+
.contentDisposition("inline")
43+
.build();
44+
45+
RequestBody requestBody = RequestBody.fromBytes(image.getBytes());
46+
s3Client.putObject(request, requestBody);
47+
return key;
48+
}
49+
50+
public void deleteImage(String key) throws IOException {
51+
final S3Client s3Client = awsConfig.getS3Client();
52+
53+
s3Client.deleteObject((DeleteObjectRequest.Builder builder) ->
54+
builder.bucket(bucketName)
55+
.key(key)
56+
.build()
57+
);
58+
}
59+
60+
61+
private String generateImageFileName() {
62+
return UUID.randomUUID() + ".jpg";
63+
}
64+
65+
66+
private void validateExtension(MultipartFile image) {
67+
String contentType = image.getContentType();
68+
if (!IMAGE_EXTENSIONS.contains(contentType)) {
69+
throw new RuntimeException("이미지 확장자는 jpg, png, webp만 가능합니다.");
70+
}
71+
}
72+
73+
private static final Long MAX_FILE_SIZE = 5 * 1024 * 1024L;
74+
75+
private void validateFileSize(MultipartFile image) {
76+
if (image.getSize() > MAX_FILE_SIZE) {
77+
throw new RuntimeException("이미지 사이즈는 5MB를 넘을 수 없습니다.");
78+
}
79+
}
80+
81+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.sopt.jaksim.global.config;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
7+
import software.amazon.awssdk.regions.Region;
8+
import software.amazon.awssdk.services.s3.S3Client;
9+
10+
@Configuration
11+
public class AwsConfig {
12+
13+
private static final String AWS_ACCESS_KEY_ID = "aws.accessKeyId";
14+
private static final String AWS_SECRET_ACCESS_KEY = "aws.secretAccessKey";
15+
16+
private final String accessKey;
17+
private final String secretKey;
18+
private final String regionString;
19+
20+
public AwsConfig(@Value("${aws-property.access-key}") final String accessKey,
21+
@Value("${aws-property.secret-key}") final String secretKey,
22+
@Value("${aws-property.aws-region}") final String regionString) {
23+
this.accessKey = accessKey;
24+
this.secretKey = secretKey;
25+
this.regionString = regionString;
26+
}
27+
28+
29+
@Bean
30+
public SystemPropertyCredentialsProvider systemPropertyCredentialsProvider() {
31+
System.setProperty(AWS_ACCESS_KEY_ID, accessKey);
32+
System.setProperty(AWS_SECRET_ACCESS_KEY, secretKey);
33+
return SystemPropertyCredentialsProvider.create();
34+
}
35+
36+
@Bean
37+
public Region getRegion() {
38+
return Region.of(regionString);
39+
}
40+
41+
@Bean
42+
public S3Client getS3Client() {
43+
return S3Client.builder()
44+
.region(getRegion())
45+
.credentialsProvider(systemPropertyCredentialsProvider())
46+
.build();
47+
}
48+
}

jaksim/src/main/java/org/sopt/jaksim/user/api/UserApiController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ public ResponseEntity<BaseResponse<?>> signup(@RequestBody final UserSignUpReque
3232
}
3333

3434
@Override
35-
@PostMapping("/user/signin")
35+
@PostMapping("/users/signin")
3636
public ResponseEntity<BaseResponse<?>> signin(@RequestHeader(AUTHORIZATION) final String accessToken) {
3737
final UserSignInResponse response = userFacade.signin();
3838
return ApiResponseUtil.success(SuccessMessage.USER_SIGN_IN_SUCCESS, response);
3939
}
4040

41-
@PostMapping("/reissue")
41+
@PostMapping("/users/reissue")
4242
public ResponseEntity<BaseResponse<?>> reissue(@RequestHeader(AUTHORIZATION) final String refreshToken,
4343
@RequestBody final UserReissueRequest userReissueRequest) {
4444
UserSignInResponse response = userFacade.reissue(refreshToken, userReissueRequest);

0 commit comments

Comments
 (0)