18
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS S3にCognitoの認証を用いてモバイルアプリからアップロード

Posted at

やりたい事

AndroidやiOSなどのモバイルアプリケーションからデータをクラウド上にアップロードしたい場合があるかと思います。以下のような要件をみたすアーキテクチャーを実現したいと考えています。

  • データをアップロードするS3のバケットは公開されいない。保存されたデータも公開されない。
  • IAMのアクセスキーをアプリケーションにハードコーディングするといった、セキュリティ上問題がある事は行わない。

上記を実現するためにCognitoを用いて、Androidアプリケーションに一時的なクレデンシャルを与えて、許可されたS3のバケットにファイルをアップデートする仕組みを作ります。

S3バケットの準備

データアップロード様のS3バケットを準備します。このバケットは、中に格納されるオブジェクトが謝って公開されない様に、バケットのパブリックアクセスコントロールリスト(ACL)を全て設定した状態で作成します。

スクリーンショット 2019-03-16 15.41.03.png

上記の設定をして置くと、バケットの中のオブジェクトを公開しようとしても、下記の様にエラーになります。バケットのACLの方がオブジェクトのACLより優先されるという事かと思います。

スクリーンショット 2019-03-16 15.49.20.png

アプリケーションからのデータは一定期間だけ保存すれば良い場合は、バケットに対してライフサイクルを設定する事ができます。
この例では、90日以上経過したデータは削除する設定にしています。ライフサイクルのルールは最大1000個まで設定可能な模様です。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/intro-lifecycle-rules.html

スクリーンショット 2019-03-16 15.56.44.png

スクリーンショット 2019-03-16 16.02.30.png

Cognitoの設定

CognitoのIDプールを作成します。Cognitoは通常、FacebookやGoogleなどのユーザー認証を簡単に実現する事ができますが、今回はアプリケーションでログインさせるのではなく、認証なしでアプリケーションにS3の権限を与えます。よって認証プロバイダーは設定しません。

スクリーンショット 2019-03-16 16.21.31.png

次にロールの設定で、先ほど作成したS3のバケットに権限を与えます。unauthenticated identitiesに対してロールを作成し、以下の様な設定を行います。

スクリーンショット 2019-03-16 16.23.06.png

以下のポリシーでは、my-mobile-app-dataバケット以下のオブジェクトに対して全ての権限が与えられています。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "mobileanalytics:PutEvents",
        "cognito-sync:*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
        "Effect": "Allow",
        "Action": [
            "s3:*Object"
        ],
        "Resource": [
            "arn:aws:s3:::my-mobile-app-data/*"
        ]
    }
  ]
}

IDプールを作成すると、以下の様にAndroidで実装するためのサンプルを表示してくれます。

スクリーンショット 2019-03-16 16.32.14.png

Androidアプリの実装

AWSのSDKを利用しますので、project直下のbuild.gradleにクラスパスを追加します。

dependencies {
    classpath 'com.amazonaws:aws-android-sdk-appsync-gradle-plugin:2.+'

次に必要となる、ライブラリをapp/build.gradleに設定します。

dependencies {
    implementation 'com.amazonaws:aws-android-sdk-core:2.+'
    implementation 'com.amazonaws:aws-android-sdk-cognito:2.+'
    implementation 'com.amazonaws:aws-android-sdk-s3:2.+'

この例では/sdcard直下のtest.jpegをアップロードしますので、インターネット関連のパーミッションに加えて、外部ストレージアクセスのパーミッションを設定します。パーミッションだけではうまく行かない場合があるので、Androidの設定画面のストレージから作成したアプリにアクセス権を与えて下さい。

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

以下、簡単にプログラムを書きました。シンプルにするためにMain ActivityのonCreateの中でAsyncTaskを作って、その中でS3のバケットにアップデートします。Cognitoで一時的なクレデンシャルを取得して、その後にS3 Clientを作成してファイルをアップロードしています。 以下のコードでS3のバケットにアップロードされる事を確認しました。

    private static class FileUploadTask extends AsyncTask <Integer, Integer, Integer> {
        private static final String BUCKET = "my-mobile-app-data";
        private static final String TEST_FILE = Environment.getExternalStorageDirectory().getPath() + "/test.jpeg";

        private final Context mContext;
        private FileUploadTask(Context context) {
            mContext = context;
        }

        @Override
        protected Integer doInBackground(Integer... value) {
            CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
                    mContext,
                    <ID Pool>, // ID プールの ID
                    Regions.AP_NORTHEAST_1 // リージョン
            );

            AmazonS3Client s3 = new AmazonS3Client(credentialsProvider, Region.getRegion(Regions.AP_NORTHEAST_1));
            TransferUtility transferUtility = TransferUtility.builder().s3Client(s3).context(mContext).build();
            File file = new File(TEST_FILE);
            TransferObserver transferObserver = transferUtility.upload(BUCKET, "test.jpeg", file);
            transferObserver.setTransferListener(new TransferListener() {
                @Override
                public void onStateChanged(int id, TransferState state) {
                }

                @Override
                public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
                }

                @Override
                public void onError(int id, Exception ex) {
                }
            });
            return 0;
        }

        @Override
        protected void onPostExecute(Integer result) {
        }
    }
スクリーンショット 2019-03-16 21.43.55.png
18
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?