Froala Editorで挿入した画像をAWS S3バゲットに保存する with Nuxt.js

# TL;DR
* Froala Editor美しい...
* Froala Editorの画像をAWS S3に保存したい →（できる）
* S3に保存した画像はパブリックにしたくない（ただし特定のドメインからのみ取得させたい）→（できる）
* ⚠️注意点⚠️
    * 画像ファイルの保護という観点で、セキュリティ面が甘い可能性があります。（ご指摘いただけると嬉しいです）
    * Froala Editorは本番環境で使う場合には有償になります。

# S3バケットを作成する
* [公式ガイドの「バケットの作成」](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/gsg/CreatingABucket.html)に従ってバケットを作成します。
* このとき、「アクセス権限」の「ブロックパブリックアクセスのバケット設定」のチェックは全て外します（後ほど別の方法でアクセスを制限します）。

# S3バケットのCross-origin resource sharing設定
* 指定したドメインからのアクセスを許可するために、Cross-origin resource sharing（CORS）の設定を行います。
* 詳細は[公式ガイドの「Cross-Origin Resource Sharing (CORS)」](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/cors.html)を参照してください。具体的な手順は[こちら](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/user-guide/add-cors-configuration.html)に書いてあります。
    * CORS設定を行うバケットを管理画面で選択します。
    * アクセス権限→CORSの設定を選択します。
    * 以下のようにXML形式でルールを記述します。ALLOWED_URLの部分にアクセス元を記述します。
    * localでテストしている時は、「 http://localhost:PORT番号 」とすればOKです。

```
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>ALLOWED_URL</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>
```

# S3バケットのアクセスキーとシークレットキーを取得する
* 管理コンソール右上のユーザー名をクリックし、「マイセキュリティ資格情報」をクリックします。
* 左側のメニューから「ユーザー」を選択し、ユーザーを追加をクリックします。
* ユーザー名を入力し、「プログラムによるアクセス」にチェックを入れます。
* アカウント権限を次の画像のように設定します。ポリシーは「AmazonS3FullAccess」を選択します。
* タグの設定は必須ではないためスキップします。
* 完了後にアクセスキーとシークレットキーが表示されます。
![スクリーンショット 2020-05-09 17.37.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324285/29c5732e-9625-aee3-427f-fd6e5fceee1c.png)
![スクリーンショット 2020-05-09 17.41.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324285/e90d5e4e-f4a4-b6a1-6b1b-20798c19e637.png)



# NuxtプロジェクトへのFroala Editor導入

Nuxtプロジェクトを作成済みの前提で以下を実施していきます。

必要なモジュールをインストールします。

```shell
npm install wysiwyg-editor-node-sdk vue-froala-wysiwyg spawn-sync cors --save
```

nuxt.config.jsに以下を記述します。

```javascript
export default {
  plugins: [
    '~/plugins/froala.js',
  ],
//...(中略)...
  build: {
    extend (config, ctx) {
      config.node = {
        fs: "empty",
        child_process: "empty",
      }
    }
  }
}


```

plugins/froala.jsを作成します。

```javascript
//plugins/froala.js
import Vue from 'vue'
import 'froala-editor/js/plugins.pkgd.min.js';
import 'froala-editor/js/third_party/embedly.min';
import 'froala-editor/js/third_party/image_tui.min';
import 'froala-editor/css/froala_editor.pkgd.min.css';
import 'froala-editor/js/plugins/image.min.js';
import VueFroala from 'vue-froala-wysiwyg';

Vue.use(VueFroala)
```

pages/froala-editor.vueを作成します。簡単のためにアクセスキーとシークレットキーを直書きしていますが、[Froala Editor公式ページ](https://froala.com/wysiwyg-editor/docs/sdks/nodejs/image-s3-upload/)にも書かれているように、別にAPIを用意してそこからHashを取得するようにした方が良いです。

```vue
<template>
  <div>
    <froala id="edit" :tag="'textarea'" :config="config" v-model="model"></froala>
  </div>
</template>

<script>
export default {
  data: () => ({
    model: 'Edit Your Content Here!',
    config: {},
  }),
  created () {
    var FroalaEditor = require('wysiwyg-editor-node-sdk/lib/froalaEditor.js')

    var configs = {
      // バケット名.
      bucket: 'YOUR-BUCKET-NAME',

      // S3バケットのリージョン：デフォルトはus-east-1.
      region: 'ap-northeast-1',

      // 画像を格納するディレクトリ名 (末尾のスラッシュが必要です).
      keyStart: 'uploads/',

      // File access.
      acl: 'public-read',

      // AWS keys.
      accessKey: 'YOUR-ACCESS-KEY', //上で取得したもの
      secretKey: 'YOUR-SECRET-KEY', //上で取得したもの
    }

    this.config = {
      imageUploadToS3: FroalaEditor.S3.getHash(configs),
      events: {
        initialized: function () {
          console.log('initialized')
        }
      }
    }
  }
}
</script>
```

Nuxtプロジェクトをbuildして実行します。

```
npm run dev
```

エディターが表示され、画像をアップロードできることを確認します。
S3のバケットにも画像ファイルがアップロードされていることを確認します。
![スクリーンショット 2020-05-10 16.10.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324285/61efc762-e73d-6a32-4987-48294230b2fc.png)

# 指定したドメイン以外からのアクセスを拒否するためのS3の設定

指定したドメイン（=私の場合は自作のWebアプリ）以外からのアクセスを拒否するための設定を行います。S3バケットにアクセスできるリファラーを指定することで、例えば直接リンクをブラウザに貼ってもアクセスできないようにすることができます。

* 「アクセス権限」の「ブロックパブリックアクセスのバケット設定」の「バケットポリシー」を選択します。
* 「バケットポリシーエディター」に以下を入力します。
* 一度パブリックアクセスを全て許可した後に、指定したリファラー以外からのアクセスを拒否します。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Public Read Bucket Objects",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::YOUR-BUCKET-NAME/*"
        },
        {
            "Sid": "Referer Deny",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::YOUR-BUCKET-NAME/*",
            "Condition": {
                "StringNotLike": {
                    "aws:Referer": "http://localhost:ポート番号/*"
                }
            }
        }
    ]
}
```

これにより、ブラウザからの画像URLへの直接アクセスが拒否されます。
![スクリーンショット 2020-05-10 16.23.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324285/24ef482c-874f-9b46-599f-77e758e0b41e.png)

試しに、適当なページ（pages/froala-viewer.vue）を用意してURLの画像を表示させてみます。

```vue
<template>
  <div>
    <img src="URL-TO-YOUR-IMAGE.png">
  </div>
</template>
```

![スクリーンショット 2020-05-10 16.28.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324285/f9ee73f4-d440-9c67-8574-a3a409e62fdb.png)

きちんと表示されました😄

# 参考

* [AWS S3のバケットポリシー設定～リファラーでの制限～](https://qiita.com/CyberMergina/items/76e4ec0970122e861cb7)