복붙노트

[SPRING] 스프링 부트 Amazon AWS S3 버킷 파일 다운로드-액세스 거부

SPRING

스프링 부트 Amazon AWS S3 버킷 파일 다운로드-액세스 거부

자동 구성된 AWS, Spring Boot 애플리케이션이 있고 Amazon S3의 지정된 버킷에서 특정 파일을 간단히 다운로드하는 엔드 포인트를 설정하려고합니다. AWS 콘솔을 사용하여 컴퓨터에서 버킷에 JPEG 파일을 업로드했습니다. 이제 Spring Boot API를 사용하여 해당 파일을 다운로드하려고합니다.

다음 오류가 발생합니다. com.amazonaws.services.s3.model.AmazonS3Exception : 액세스 거부 (서비스 : Amazon S3; 상태 코드 : 403; 오류 코드 : AccessDenied;

AWS 콘솔에서 사용자 및 그룹 (사용자가 그룹에 있음)을 생성했습니다. 사용자 / 그룹은 관리자 액세스뿐만 아니라 S3에 대한 전체 액세스 권한을 갖습니다. 액세스 키 / 비밀 키 쌍을 다운로드하고 테스트 목적으로 아래 표시된 것처럼 키를 내 application.properties 파일에 그대로 붙여 넣었습니다 (키는 분명히 표시되지 않음 :)).

왜 여전히 액세스가 거부되는지 혼란스러워합니다. 나는 이것을 잠시 동안 검색하고 연구 해 왔습니다. 스프링 부트와 관련된이 문제에 대한 해결책을 찾지 못하는 것 같습니다. 도움을 주시면 감사하겠습니다.

application.properties:

cloud.aws.credentials.accessKey=myaccesskey
cloud.aws.credentials.secretKey=mysecretkey
cloud.aws.credentials.instanceProfile=false
cloud.aws.stack.auto=false

cloud.aws.region.auto=true
cloud.aws.region.static=myregion

SimpleResourceLoadingBean.java:

@RestController
public class SimpleResourceLoadingBean {

    private static Logger log = LoggerFactory.getLogger(HealthMonitorApplication.class);

    @Autowired
    private ResourceLoader resourceLoader;


    @RequestMapping("/getresource")
    public String resourceLoadingMethod() throws IOException {
        log.info("IN RESOURCE LOADER");

        Resource resource = this.resourceLoader.getResource("s3://s3.amazonaws.com/mybucket/myfile.ext");

        InputStream inputStream = resource.getInputStream();

        return inputStream.toString();
    }
}

pom.xml (질문과 관련된 종속성 만)

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-aws</artifactId>
            <version>1.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-aws-autoconfigure</artifactId>
            <version>1.1.0.RELEASE</version>
        </dependency>

해결법

  1. ==============================

    1.해결책을 알아 냈습니다. application.properties 구성 외에도 적절한 자격 증명을 제공 할 때 AmazonS3Client 객체에 액세스 할 수있는 구성 클래스를 만들어야했습니다. 나는 GitHub 에서이 예제를 따랐다.

    해결책을 알아 냈습니다. application.properties 구성 외에도 적절한 자격 증명을 제공 할 때 AmazonS3Client 객체에 액세스 할 수있는 구성 클래스를 만들어야했습니다. 나는 GitHub 에서이 예제를 따랐다.

    https://github.com/brant-hwang/spring-cloud-aws-example/blob/master/src/main/java/com/axisj/spring/cloud/aws/AWSConfiguration.java

    AWSConfiguration.java:

    import com.amazonaws.auth.AWSCredentials;
    import com.amazonaws.auth.BasicAWSCredentials;
    import com.amazonaws.regions.Region;
    import com.amazonaws.regions.Regions;
    import com.amazonaws.services.s3.AmazonS3Client;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class AWSConfiguration {
    
        @Value("${cloud.aws.credentials.accessKey}")
        private String accessKey;
    
        @Value("${cloud.aws.credentials.secretKey}")
        private String secretKey;
    
        @Value("${cloud.aws.region}")
        private String region;
    
        @Bean
        public BasicAWSCredentials basicAWSCredentials() {
            return new BasicAWSCredentials(accessKey, secretKey);
        }
    
        @Bean
        public AmazonS3Client amazonS3Client(AWSCredentials awsCredentials) {
            AmazonS3Client amazonS3Client = new AmazonS3Client(awsCredentials);
            amazonS3Client.setRegion(Region.getRegion(Regions.fromName(region)));
            return amazonS3Client;
        }
    }
    

    이 구성이 완료되면 다른 클래스에서 AmazonS3Client 객체 (자동 유선)를 생성하고 클라이언트를 사용하여 S3 클라우드에 요청할 수 있습니다. 이 예제에서는 추가 컨트롤러 클래스를 쉽게 구현할 수 있도록 랩퍼 클래스를 서비스로 사용합니다.

    S3Wrapper.java:

    import com.amazonaws.services.s3.AmazonS3Client;
    import com.amazonaws.services.s3.model.*;
    import org.apache.commons.io.IOUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    @Service
    public class S3Wrapper {
    
        @Autowired
        private AmazonS3Client amazonS3Client;
    
        @Value("${cloud.aws.s3.bucket}")
        private String bucket;
    
        private PutObjectResult upload(String filePath, String uploadKey) throws FileNotFoundException {
            return upload(new FileInputStream(filePath), uploadKey);
        }
    
        private PutObjectResult upload(InputStream inputStream, String uploadKey) {
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, uploadKey, inputStream, new ObjectMetadata());
    
            putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);
    
            PutObjectResult putObjectResult = amazonS3Client.putObject(putObjectRequest);
    
            IOUtils.closeQuietly(inputStream);
    
            return putObjectResult;
        }
    
        public List<PutObjectResult> upload(MultipartFile[] multipartFiles) {
            List<PutObjectResult> putObjectResults = new ArrayList<>();
    
            Arrays.stream(multipartFiles)
                    .filter(multipartFile -> !StringUtils.isEmpty(multipartFile.getOriginalFilename()))
                    .forEach(multipartFile -> {
                        try {
                            putObjectResults.add(upload(multipartFile.getInputStream(), multipartFile.getOriginalFilename()));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
    
            return putObjectResults;
        }
    
        public ResponseEntity<byte[]> download(String key) throws IOException {
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, key);
    
            S3Object s3Object = amazonS3Client.getObject(getObjectRequest);
    
            S3ObjectInputStream objectInputStream = s3Object.getObjectContent();
    
            byte[] bytes = IOUtils.toByteArray(objectInputStream);
    
            String fileName = URLEncoder.encode(key, "UTF-8").replaceAll("\\+", "%20");
    
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            httpHeaders.setContentLength(bytes.length);
            httpHeaders.setContentDispositionFormData("attachment", fileName);
    
            return new ResponseEntity<>(bytes, httpHeaders, HttpStatus.OK);
        }
    
        public List<S3ObjectSummary> list() {
            ObjectListing objectListing = amazonS3Client.listObjects(new ListObjectsRequest().withBucketName(bucket));
    
            List<S3ObjectSummary> s3ObjectSummaries = objectListing.getObjectSummaries();
    
            return s3ObjectSummaries;
        }
    }
    

    참고 : Apache Commons IO 라이브러리를 사용하려면 pom.xml에 다음 종속성을 추가해야합니다.

    pom.khml :

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.3.2</version>
    </dependency>
    
  2. ==============================

    2.허용되는 답변은 더 이상 사용되지 않는 API를 사용하고 있습니다. 업데이트 된 개정판이 있습니다.

    허용되는 답변은 더 이상 사용되지 않는 API를 사용하고 있습니다. 업데이트 된 개정판이 있습니다.

    먼저 maven 종속성을 업데이트하십시오.

     <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk</artifactId>
            <version>1.11.274</version>
        </dependency>
    

    AWSConfiguration.java

    import com.amazonaws.auth.AWSCredentials;
    import com.amazonaws.auth.AWSStaticCredentialsProvider;
    import com.amazonaws.auth.BasicAWSCredentials;
    import com.amazonaws.services.s3.AmazonS3;
    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 AWSConfiguration {
    
        @Value("${cloud.aws.credentials.accessKey}")
        private String accessKey;
    
        @Value("${cloud.aws.credentials.secretKey}")
        private String secretKey;
    
        @Value("${cloud.aws.region}")
        private String region;
    
        @Bean
        public BasicAWSCredentials basicAWSCredentials() {
            return new BasicAWSCredentials(accessKey, secretKey);
        }
    
        @Bean
        public AmazonS3 amazonS3Client(AWSCredentials awsCredentials) {
            AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard();
            builder.withCredentials(new AWSStaticCredentialsProvider(awsCredentials));
            builder.setRegion(region);
            AmazonS3 amazonS3 = builder.build();
            return amazonS3;
        }
    }
    

    S3Service.java

    import com.amazonaws.services.s3.AmazonS3;
    import com.amazonaws.services.s3.AmazonS3Client;
    import com.amazonaws.services.s3.model.*;
    import org.apache.commons.io.IOUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    @Service
    public class S3Service {
    
        @Autowired
        private AmazonS3 amazonS3;
    
        @Value("${cloud.aws.s3.bucket}")
        private String bucket;
    
        private PutObjectResult upload(String filePath, String uploadKey) throws FileNotFoundException {
            return upload(new FileInputStream(filePath), uploadKey);
        }
    
        private PutObjectResult upload(InputStream inputStream, String uploadKey) {
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, uploadKey, inputStream, new ObjectMetadata());
    
            putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);
    
            PutObjectResult putObjectResult = amazonS3.putObject(putObjectRequest);
    
            IOUtils.closeQuietly(inputStream);
    
            return putObjectResult;
        }
    
        public List<PutObjectResult> upload(MultipartFile[] multipartFiles) {
            List<PutObjectResult> putObjectResults = new ArrayList<>();
    
            Arrays.stream(multipartFiles)
                    .filter(multipartFile -> !StringUtils.isEmpty(multipartFile.getOriginalFilename()))
                    .forEach(multipartFile -> {
                        try {
                            putObjectResults.add(upload(multipartFile.getInputStream(), multipartFile.getOriginalFilename()));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
    
            return putObjectResults;
        }
    
        public ResponseEntity<byte[]> download(String key) throws IOException {
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, key);
    
            S3Object s3Object = amazonS3.getObject(getObjectRequest);
    
            S3ObjectInputStream objectInputStream = s3Object.getObjectContent();
    
            byte[] bytes = IOUtils.toByteArray(objectInputStream);
    
            String fileName = URLEncoder.encode(key, "UTF-8").replaceAll("\\+", "%20");
    
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            httpHeaders.setContentLength(bytes.length);
            httpHeaders.setContentDispositionFormData("attachment", fileName);
    
            return new ResponseEntity<>(bytes, httpHeaders, HttpStatus.OK);
        }
    
        public List<S3ObjectSummary> list() {
            ObjectListing objectListing = amazonS3.listObjects(new ListObjectsRequest().withBucketName(bucket));
    
            List<S3ObjectSummary> s3ObjectSummaries = objectListing.getObjectSummaries();
    
            return s3ObjectSummaries;
        }
    }
    
  3. ==============================

    3.URL 패턴을 수정하십시오

    URL 패턴을 수정하십시오

    자원 자원 = this.resourceLoader.getResource ( "s3 : //s3.amazonaws.com/mybucket/myfile.ext");

    자원 자원 = this.resourceLoader.getResource ( "s3 : //mybucket/myfile.ext");

    문서에 따라 패턴은 s3 : // / 입니다. 스프링 부트 2.0.6.RELEASE 및 스프링 클라우드 Finchley.SR2 (확인)에서 작동합니다.

    참조 : Spring Cloud AWS-파일 다운로드

    from https://stackoverflow.com/questions/38578937/spring-boot-amazon-aws-s3-bucket-file-download-access-denied by cc-by-sa and MIT license