CORSの動作を確認した
前回、こちらのエントリーにて、XMLHttpRequestが SOP の対象となった場合の動作を確認しました。
今回は、Cross-Origin Resource Sharing (CORS)の仕様したがって、SOPで制限されている クロスオリジンへの XMLHttpRequest(XHR) を可能にします。
CORSについては以下を参照すると良いでしょう。
リクエストは2パターン
リクエストは2パターンあります。条件によってそれぞれ使い分けされます。
それぞれ動作を確認していきます。
シンプルなリクエスト
シンプルなリクエストとは
シンプルなリクエストとは、以下のすべての条件に合うものです。
※HTTP アクセス制御 (CORS)から抜粋
許可されたメソッドは以下に限る:
- GET
- HEAD
- POST
ユーザエージェントが自動的に設定するヘッダ (Connection や User-Agent など) を除き、手動で設定できるヘッダは以下に限る:
- Accept
- Accept-Language
- Content-Language
- Content-Type
Content-Type ヘッダで許可される値は以下に限る:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
実際にやってみる
前回の記事で、「異なるオリジンにXHRをしてみる」というセクションで、異なるオリジンにXHRをしSOP の制御対象となることを確認しましたが、このリクエストを許可するようにします。
前回と同じ環境で、異なるオリジンのXHRが許可されるような条件を加え試します。
前回の環境
- XHR送信側サーバ(Sinatra):
http://test.example.com:4567/
(HTML,JSをレンダリングし、そこからXHRを送信します。) - XHR受信側サーバ(Sinatra):
http://test.example.com:4568/
- リクエストメソッド: POST
- レスポンス:
Failed to load http://test.example.com:4568/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://test.example.com:4567' is therefore not allowed access.
というエラーがブラウザに表示され、Response が受け取れない
Accept:*/* Accept-Encoding:gzip, deflate, br Accept-Language:ja Connection:keep-alive Content-Length:0 Content-Type:text/plain Host:localhost:4567 Origin:http://test.example.com:4567 Referer:http://test.example.com:4567/tools User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36
加える条件
XHR受信側サーバ(http://test.example.com:4568/
) の Response Header に以下を返すようにします。
Access-Control-Allow-Origin: http://test.example.com:4567/
結果
レスポンスを受け取ることができました。
レスポンスヘッダ
Access-Control-Allow-Origin:http://test.example.com:4567 Connection:keep-alive Content-Length:116 Content-Type:text/html;charset=utf-8 Server:thin X-Content-Type-Options:nosniff X-Frame-Options:SAMEORIGIN X-XSS-Protection:1; mode=block
レスポンスボディ(レスポンスメソッド とUserAgentを返すようにしています。)
POST from [Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36]
Access-Control-Allow-Origin ヘッダ
上記の動きで確認した通り、Access-Control-Allow-Origin
は、指定した Origin のアクセスを許可することができます。
リクエストで送られたOrigin
ヘッダの内容とこの Access-Control-Allow-Origin
が同じであればアクセス許可されるという仕組みです。
(Origin
ヘッダは、XHRで異なるOrigin
へ送信するときに自動的に付与されます。)
どの Origin でもアクセスを許可させる場合は、Access-Control-Allow-Origin: *
という形にもできます。
ただし、これは、後ほど説明する クレデンシャルを含むリクエスト
には使うことができません。
プリフライトリクエスト
以下のようなリクエストのときに、プリフライトリクエストを行います。
GET、HEAD、POST 以外のメソッドを使用した場合。また application/x-www-form-urlencoded、multipart/form-data、または text/plain 以外の Content-Type とともに POST を使用してリクエストを行う場合、例えば application/xml または text/xml を使用して POST で XML のペイロードをサーバーへ送るときは、リクエストでプリフライトを行います。
実際にやってみる
ひとまず、ダメ元で、先ほどシンプルなリクエストで行ったもののリクエストメソッドをPOSTからPUTに変更しただけの状態で試してみます。
デベロッパーツールのConsoleには、以下のようなエラーが表示されました。
xhr.js:18 OPTIONS http://test.example.com:4568/ net::ERR_ABORTED send_request @ xhr.js:18 onclick @ tools:22 tools:1 Failed to load http://test.example.com:4568/: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://test.example.com:4567' is therefore not allowed access. The response had HTTP status code 404. xhr.js:18 XHR failed loading: OPTIONS "http://test.example.com:4568/".
OPTIONSメソッドでアクセスしようとしたが、サーバ側でOPTIONSを受け取る処理を行っていなかったので、404で返ってきているみたいですが、 なぜ、PUTではなく、OPTIONSで送信されたのでしょう。
プリフライトリクエストの流れ
OPTIONSで送信されたのには、以下の理由があります。
HTTP アクセス制御 (CORS)には、こう記述されています。
シンプルなリクエスト (前述) とは異なり、"プリフライト" リクエストは始めに、実際のリクエストを送信しても安全かを確かめるために他ドメインのリソースへ向けて OPTIONS メソッドを使用して HTTP リクエストを送信します。クロスサイトリクエストはユーザーデータに影響を与える可能性があるため、このようにプリフライトを行います。
つまり、流れとしては、
- [クライアント]OPTIONSメソッドを送信
- [サーバ]許可しているメソッドやカスタムヘッダをResponse Headerに含め返します。
- メソッドを許可: Access-Control-Request-Method
カスタムヘッダを許可: Access-Control-Request-Headers (こちらは後ほど詳しく説明します。)
[クライアント]元々送ろうとしていたリクエストを送信する
・・・あとは、通常通り
改めて実際にやってみる
今回は、PUTメソッドを許可するので、サーバ側に以下の設定を加えます。
- OPTIONSメソッドを受け取るようにする
- OPTIONSメソッドを受け取ったら、以下のヘッダ・値を返すようにする
Access-Control-Allow-Origin: http://test.example.com:4567
Access-Control-Allow-Methods: PUT
そして送信!
デベロッパーツールのConsoleには、以下のように、OPTIONSとPUTを送信している様子が見えます。
xhr.js:18 XHR finished loading: OPTIONS "http://test.example.com:4568/". send_request @ xhr.js:18 onclick @ tools:22 XHR finished loading: PUT "http://test.example.com:4568/".
リクエストヘッダ(OPTIONS)
OPTIONS / HTTP/1.1 Host: localhost:4568 Connection: keep-alive Access-Control-Request-Method: PUT Origin: http://test.example.com:4567 User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36 Accept: */* Referer: http://test.example.com:4567/tools Accept-Encoding: gzip, deflate, br Accept-Language: ja,en-US;q=0.8,en;q=0.6
レスポンスヘッダ(OPTIONS)
HTTP/1.1 200 OK Content-Type: text/html;charset=utf-8 Access-Control-Allow-Origin: http://test.example.com:4567 Access-Control-Allow-Methods: PUT X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Connection: close Server: thin
リクエストヘッダ(PUT)
PUT / HTTP/1.1 Host: localhost:4568 Connection: keep-alive Content-Length: 0 Accept: */* Origin: http://test.example.com:4567 Accept-Language: ja User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36 Content-Type: text/plain Referer: http://test.example.com:4567/tools Accept-Encoding: gzip, deflate, br
レスポンスヘッダ(PUT)
HTTP/1.1 200 OK Content-Type: text/html;charset=utf-8 Access-Control-Allow-Origin: http://test.example.com:4567 Content-Length: 115 X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Connection: keep-alive Server: thin
うまくリクエストを送ることができました。
クレデンシャルを含むリクエスト
同一オリジンであれば、クレデンシャルを含むリクエストはデフォルトで許可されますが、 異なるオリジンへクレデンシャルを含むリクエストを送るときは、注意が必要です。
ちなみに、クレデンシャルというのは、Cookieの送信、BASIC認証を指します。
実際にやってみる
ひとまず、そのままではできないことを確認しましょう。
こんな感じで、XHRでCookieを含めて送信するようにします。
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
デベロッパーツールのConsoleには、以下のようなエラーが表示されました。
tools:1 Failed to load http://test.example.com:4568/: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. Origin 'http://test.example.com:4567' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute. xhr.js:19 XHR failed loading: POST "http://test.example.com:4568/".
なるほど、Access-Control-Allow-Credentials
ヘッダを追加というものをつけて上げる必要があると
というわけで、サーバに以下のヘッダを返すようにします。
Access-Control-Allow-Credentials:true
注意点としては、上記を追加する場合は、Access-Control-Allow-Origin:*
の指定ができません。
必ず、オリジンを指定する必要があります。
改めて実際にやってみる
では、引き続きやってみましょう。
シンプルなリクエスト・プリフライトリクエストが必要なものどちらも行いましたが、今度は、成功しました。
デベロッパーツールのConsole
xhr.js:19 XHR finished loading: POST "http://test.example.com:4568/". send_request @ xhr.js:19 onclick @ tools:21 xhr.js:19 XHR finished loading: OPTIONS "http://test.example.com:4568/". send_request @ xhr.js:19 onclick @ tools:22 XHR finished loading: PUT "http://test.example.com:4568/".
CORSのヘッダについて
CORSのリクエストヘッダ/レスポンスヘッダの詳細については、HTTP アクセス制御 (CORS)に詳しく書いてあるので、ここを読めばバッチリでしょう。
参考
XMLHttpRequestが Same-Origin Policy の対象となった場合の動作を確認した
XMLHttpRequest(以降「XHR」と表記)が Same-Origin Policyの対象となった場合の動作を実際にリクエストを送信し、動作を確認しました。
今回はSame-Origin Policy周りの検証をしていくにあたっての第一弾として、今後はもう少し踏み込んだ内容でやっていく予定です。
Same-Origin Policy(SOP)とは
Same-Origin Policy(以降「SOP」と表記) は、日本語では、同一オリジンポリシー
、同一生成元ポリシー
などと呼ばれています。
あるオリジン(スキーム、ホスト、ポートの組み合わせ)から読み込まれた文書やスクリプトについて、そのリソースから他のオリジンのリソースにアクセスできないように制限するものです。
逆に、同じオリジンであれば、制限なしにアクセスが行えます。
今回は、XHRのSOPの動作を確認をしていきますが、他には、DOMアクセス、Cookieに関するSOP。プラグインなどでは、Java,Flash、Silverlightなどにもそれぞれ個別のSOPが存在します。
詳しくは、MDN web docsのこちらのページが参考になります。
動作を確認する
簡単な検証用webアプリケーションをSinatraで作成しました。
アプリケーションは以下のような機能をもっています。
- 画面から好きなURI宛にXHRをGet、Postで送信することができる。
- 画面からXHRの結果(Response BodyとStatus Code)を見ることができる。
- Response Bodyには、送った Request Method とUserAgentを表示します。
- ルートパスにGet、Postリクエストを送ることができ、リクエストの内容をコンソールに表示することができる。
画面はこんな感じです。バッと雑に作って確認していきます。
これを、先ほど説明したアプリケーションをそれぞれポートを変えて2つ起動し、XHRでやり取りできるか、できないかを確認します。
http://test.example.com:4567/
http://test.example.com:4568/
※ホスト名は検証したものとは異なります。
同一のオリジンにXHRをしてみる
まずは、アプリケーションの機能で、画面から自分宛にXHRを送信します。
http://test.example.com:4567/tool
にアクセスすると、
画面から好きなURI宛にXHRをGet、Postで送信することができるツールが開くので、
ここから、http://test.example.com:4567/
宛にXHRを送信します。
結果としては、もちろんですが、リクエストの送信・レスポンスの取得が成功しました。
XHR finished loading: GET "http://test.example.com:4567/". XHR finished loading: POST "http://test.exapmle.com:4567/".
- サーバ側にXHRが届いていることを確認
GET from [Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36] XXX.XXX.XXX.XXX - - [XX/Sep/2017:XX:XX:XX +0900] "GET / HTTP/1.1" 200 - 0.0038 POST from [Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36] "Content-Type [text/plain]" "Body [Request Body]" XXX.XXX.XXX.XXX - - [XX/Sep/2017:XX:XX:XX +0900] "POST / HTTP/1.1" 200 19 0.0005
- 検証用のアプリケーションの画面からも確認
異なるオリジンにXHRをしてみる
ポートを変えて同じアプリケーションを起動し、そこへXHRを送信してみます。
- デベロッパーツールのConsoleから確認
要求されたリソースに Access-Control-Allow-Origin
ヘッダーがないので、アクセスが許可されていない旨のメッセージがブラウザに表示されました。(Get、Post両方とも同じ内容)
Failed to load http://test.example.com:4568/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://test.example.com:4567' is therefore not allowed access.
- リクエスト自体は、サーバ側に届いているようです。
GET from [Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36] XXX.XXX.XXX.XXX - - [XX/Sep/2017:XX:XX:XX +0900] "GET / HTTP/1.1" 200 - 0.0005 POST from [Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36] "Content-Type [text/plain]" "Body [Request Body]" XXX.XXX.XXX.XXX - - [XX/Sep/2017:XX:XX:XX +0900] "POST / HTTP/1.1" 200 19 0.0005
- 検証用のアプリケーションの画面からも確認
Get、Postどちらも値が取得できませんでした。
まとめ
異なるオリジンへは、特に対策を行わない限りXHRでResponseの値を取得することができませんでした。 また、リクエストはサーバ側に届きますが、クライアント側でレスポンスは取得できないということがわかりました。
参考
SOPに関しては、以下を参考にしました。
TwitterAPIを使ってOAuth2.0のフロー(Client Credentials)を確認した。
先日、以下のエントリーにて、4つあるOAuth2.0の認可フロー(Grant Type)からAuthorization Codeの流れを確認しました。
今回は、TwitterAPIにOAuth2.0でツイートの検索ができるよう認可してもらい、実際に検索結果のjsonが返ってくるところまでやっていきたいと思います。
Client Credentialsの流れは動画で確認できます。
参考
OAuth2.0(Client Credentials)について
Twitterの認可・仕様などについて
やること・ゴール
- 前回同様、Rubyで簡易的な実装で流れを掴んで行きたいと思います。
- 上記にも書きましたが、TwitterAPIにOAuth2.0でツイートの検索ができるよう認可してもらい、実際に検索結果のjsonが返ってくるところまでやっていきたいと思います。
Client Credentialsについて
Client Credentialsは他の認証フロー3つとは、少し異なります。
- ユーザ
- リソースを保持するサービス・認可をするサービス
- 第三者(リソースを保持するサービスのリソースを使いたい)
上記3人の登場人物がいたとすると、Client Credentials以外の認証フロー(Authorization Code, Implicit, Resource owner password credentials)は、 第三者がリソースへのアクセスの認可をしてもらうためのものです。 それに対して、Client Credentialsは認可を必要としていないリソースへアクセスする場合に使用します。
具体的にどういうこと?というと、 例えば、今回実装するTwitterAPIでは、 ‘Application-only authentication’ という認可のタイプ、以下の操作をすることが許可されます。 (TwitterAPIでは、この認可タイプ以外は、OAuth2.0に対応していないようです。)
- ツイートの検索
- ユーザのツイートの取得
- ユーザー情報を取得
- 任意のアカウントの友人やフォロワーの取得
など
反対にアクセスできないものは、
- ツイートの投稿
- ダイレクトメッセージの送信
など
つまり、ユーザ認可が必要のない公開されている内容のみ使用したい場合に使います。
恐らくアクセス数の制御のためだと思われます。
大まかな流れ
Client Credentials の説明を上記で行った通り、今回TwitterAPIでは、 ‘Application-only authentication’ の認可をもらいます。
流れとしては以下です。公式ページの流れに沿って実装していきます。
Application-only authentication — Twitter Developers
- consumer_keyとconsumer_secretを取得する
- consumer_keyとconsumer_secretをBase64でエンコードし、bearer_tokenを取得する
- bearer_tokenを使って、特定のアカウントのツイート一覧を表示する
consumer_keyとconsumer_secretを取得する
Twitter Developersにログインし、 consumer_keyとconsumer_secretをもらいます。 手順は検索すると多くの記事がでてくるので割愛します。
consumer_keyとconsumer_secretをBase64でエンコードし、bearer_tokenを取得する
ここは公式ページの Step 1: Encode consumer key and secret
, Step 2: Obtain a bearer token
に該当します。
以下のコードにあるように、consumer_key, consumer_secretをURIエンコードし、 :
で結合後、Base64エンコーディングし、リクエストを送信します。
# -*- coding: utf-8 -*- require 'uri' require 'net/http' require 'openssl' require 'base64' consumer_key = URI.encode({ consumer_key }) consumer_secret = URI.encode({ consumer_secret }) credential = Base64.strict_encode64(consumer_key + ':' + consumer_secret) uri = URI.parse('https://api.twitter.com/oauth2/token') https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true request_body_params = 'grant_type=client_credentials' request_header = { 'Authorization': "Basic #{credential}", 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Content-Length': request_body_params.length.to_s, 'Accept-Encoding': 'gzip' } request = Net::HTTP::Post.new(uri.request_uri, request_header) request.body = request_body_params response = https.request(request) puts response.code puts response.body
成功するとステータスコード200で以下のレスポンスが返ってきます。
access_token
が bearer_token
です。
{ "token_type":"bearer", "access_token": { ここにアクセストークンが表示されます。 } }
bearer_tokenを使って、特定のアカウントのツイート一覧を表示する
ここは公式ページの Step 3: Authenticate API requests with the bearer token
に該当します。
ひとまず、公式のサンプル通りに実装していきます。
# -*- coding: utf-8 -*- require 'uri' require 'net/http' access_token = { access_token } uri = URI.parse('https://api.twitter.com/1.1/statuses/user_timeline.json') https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true request_header = { 'Authorization': "Bearer #{access_token}", 'Accept-Encoding': 'gzip' } request_params = { count: '100', screen_name: 'twitterapi' } uri.query = URI.encode_www_form(request_params) request = Net::HTTP::Get.new(uri.request_uri, request_header) response = https.request(request) puts response.code puts response.body
成功するとステータスコード200でそれっぽいレスポンスがjsonで返ってきます。
せっかくなので、自分のツイートの検索してみます。 パラメータの仕様はこちらで確認しました。
GET search/tweets — Twitter Developers
# -*- coding: utf-8 -*- require 'uri' require 'net/http' access_token = { access_token } uri = URI.parse('https://api.twitter.com/1.1/search/tweets.json') https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true request_header = { 'Authorization': "Bearer #{access_token}", 'Accept-Encoding': 'gzip' } request_params = { q: '腹痛に耐えながらnginxをビルドし直し自サービスをhttp2化した。', lang: 'ja', result_type: 'mixed', count: '1' } uri.query = URI.encode_www_form(request_params) request = Net::HTTP::Get.new(uri.request_uri, request_header) response = https.request(request) puts response.code puts response.body
結果のjsonはちゃんと見ていませんが screen_name
に自分のアカウントが表示されていたので良さそうな気がします。
PicasaAPIを使ってOAuth2.0のフロー(Authorization Code)を確認した。
OAuth、書籍やネット上の記事などから大まかに仕組みは理解していたのですが、具体的にリクエスト/レスポンスをどう送り合ってやりとりを行っているのかがよくわかっていませんでした。 OAuthの一連の流れを理解するために、今回は実際にOAuth2.0から認可してもらう流れをライブラリを使わずに実装していきたいと思います。
OAuth1.0はsignetureを作るのが少しメンドクサそうだったので、今回はOAuth2.0だけやります。
参考
OAuth2.0について
Google Photosへのアップロードについて
やること・ゴール
- Rubyで簡易的な実装で流れを掴んで行きたいと思います。
- Google PhotosにOAuth2.0で画像をアップロードできるよう認可してもらい、実際に画像をアップロードできるようにするところまでやっていきたいと思います。 (自宅のPCはUbuntuを使っているのですが、Google PhotosのクライアントはWindowsとMacしかないので、今後自作でちゃんとしたクライアント作っていくために、今回Google Photosへアップロードする流れを確認しました。)
- Google Photosは統合前のサービスPicasa、このAPIが使えます。(Google Photosというのは残念ながらないようです。)
OAuth2.0の認可フロー(Grant Type)
OAuth2.0の認可フローには4種類あります。 各フローにyoutubeに解説の動画があるので、リンクを貼っておきます。
Authorization Code 動画:https://www.youtube.com/watch?v=j0pIlZdD7-A&feature=youtu.be
- サーバサイドで多くの処理を行うウェブアプリケーション向けのフロー
Implicit 動画:https://www.youtube.com/watch?v=fF1mJB64xAg
- スマホアプリやJSを用いたクライアント再度で処理の多くを行うアプリケーション向けのフロー
Resource owner password credentials 動画:https://www.youtube.com/watch?v=qS0YY9Fo04Q
- サーバサイドを利用しないアプリケーション向けのフロー
Client Credentials 動画:https://www.youtube.com/watch?v=LLIMOYk5Syc
- ユーザ単位での認可を行わないアプリケーション向けのフロー
今回は、今後WEBアプリケーションで使っていけるよう、上記フローのうちAuthorization Codeで実装します。
googleからクライアント ID、クライアントシークレットをもらう
Google APIs Consoleへログインし、クライアント ID、クライアント シークレットをもらいます。 手順は検索すると多くの記事がでてくるので割愛します。
今回はAuthorization Codeで実装を行うので、アプリケーションの種類
はウェブ アプリケーション
を選択しました。
承認済みのリダイレクト URI
は、ユーザが認可のためGoogleにログイン後にリダイレクトされるURIなのですが、ここにクエリとしてアクセストークンがくっついてきます。
その他
にすると、リダイレクトされず、トークンが画面に表示されるようになります。
(Android、Chromeアプリ、iOS、PlayStation4にするとどういう動きになるかがわかりませんが、気になる方は調べてみると良いでしょう。)
認可コード(Authorization Code)をもらう
以下のコードで、認可コード(Authorization Code)をもらうためのログイン画面を表示します。 ここでは、uriの組み立てだけなのですが、hashでいい感じにクエリ付のuriを生成してくれるので、net/httpを使って実装しています。 (リクエストは送っていません。)
パラメータの仕様は以下です。 https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
state
も指定できるので、WEBアプリケーションを実装するときにはつけておいた方が良いでしょう。
# -*- coding: utf-8 -*- require 'uri' require 'net/http' client_id = { クライアントID } redirect_uri = { リダイレクトURI } # Google APIs Consoleで登録したリダイレクトURIを入力します。異なる場合を入れた場合はエラーになります。 scope = 'https://picasaweb.google.com/data/' # Google Photosはpicasaのapiを利用しているので、次のURLを入れます。 uri = URI('https://accounts.google.com/o/oauth2/v2/auth') params = { response_type: 'code', client_id: client_id, redirect_uri: redirect_uri, scope: scope } uri.query = URI.encode_www_form(params) puts uri.to_s
ログインが完了したら、redirect_uriで指定したuriにリダイレクトされます。 その際に、クエリにcode={Authorization Code}がついてくるので、それをコピーしておきます。 WEBアプリケーションを実装する場合は、コールバック用のuriを用意しておき、Authorization Codeを取得できるようにしておくと良さそうです。
アクセストークンを取得する
以下のリクエストで、アクセストークンを取得します。 WEBアプリケーションを選択した場合は、有効期限はないため、リフレッシュトークンは発行されません。
# -*- coding: utf-8 -*- require 'uri' require 'net/http' require 'openssl' authorization_code = { 先ほど取得したauthorization_code } client_id = { クライアントID } client_secret = { クライアントシークレット } redirect_uri = { リダイレクトURI } uri = URI.parse('https://www.googleapis.com/oauth2/v4/token') https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data({ code: authorization_code, client_id: client_id, client_secret: client_secret, redirect_uri: redirect_uri, grant_type: 'authorization_code' }) response = https.request(request) puts response.code puts response.body
アクセストークンを使って、画像アップロード
以下のコードで画像をアップロードしてみます。
# -*- coding: utf-8 -*- require 'uri' require 'net/http' require 'openssl' user_id = { アップロードするユーザのid } access_token = { アクセストークン } upload_file = 'sample.jpg' uri = URI.parse("https://picasaweb.google.com/data/feed/api/user/#{user_id}/?access_token=#{access_token}") https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true request_header = { 'Content-Type': "image/jpeg", 'Content-Length': File.size(upload_file).to_s, 'Slug': upload_file } request = Net::HTTP::Post.new(uri.request_uri, request_header) request.body = File.read(upload_file) response = https.request(request) puts response.code puts response.body
アップロードされた画像を確認する
Google Photesアクセスし、画像がアップロードされていることを確認します。
OAuth2.0でGoogle PhotosへのアクセスをGoogleに認可してもらい、実際にアップロードできるところまで確認しました。 Authorization Code以外の認可フローについてはまた別の機会にやろうと思います。
RHEL7でRailsサーバを構築した サーバの基本設定
こちらは、
RHEL7でRailsアプリのサーバを構築した - takapiのブログ
の サーバの基本設定
に関する記事です。
OS(RHEL7)のアップデート
まずは、yum コマンドでOS(RHEL7)を更新します。
$ sudo yum -y update
ロケールの確認・変更
RHEL7からは、ロケール設定の確認・変更は 'localectl' コマンドを使用するようです。
こちらのサイトを参考に確認・変更します。 http://zero-config.com/centos/changelocale-002.html
現在の状態を確認します。
$ localectl status System Locale: LANG=ja_JP.utf8 VC Keymap: us X11 Layout: us
EC2で設定したRHEL7の System Locale
はデフォルトで日本語になっておりました。
VC Keymap、 X11 Layout
については、SSHからの接続のみなので無視して良さそうです。
ちなみに、 System Locale
の修正は以下のように行います。
sudo localectl set-locale LANG=ja_JP.utf8 # ja_JP.utf8で設定を行う場合
日付の変更
RHEL7からは、システムの日付および時間に関する情報の設定は 'timedatectl' コマンドを使用するようです。
こちらのサイトを参考に確認・変更します。
第2章 日付と時刻の設定 - Red Hat Customer Portal
現在の状態を確認します。
$ timedatectl status Local time: 土 2017-06-24 01:24:37 EDT Universal time: 土 2017-06-24 05:24:37 UTC RTC time: 土 2017-06-24 05:24:37 Time zone: America/New_York (EDT, -0400) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: yes Last DST change: DST began at 日 2017-03-12 01:59:59 EST 日 2017-03-12 03:00:00 EDT Next DST change: DST ends (the clock jumps one hour backwards) at 日 2017-11-05 01:59:59 EDT 日 2017-11-05 01:00:00 EST
どうやら Time zone
が America/New_York
になっているようなので、タイムゾーンの変更します。
$ sudo timedatectl set-timezone Asia/Tokyo
もう一度、 timedatectl status
を実行し Time zone
が Asia/Tokyo
になっていることを確認します。
$ timedatectl status Local time: 土 2017-06-24 14:29:26 JST Universal time: 土 2017-06-24 05:29:26 UTC RTC time: 土 2017-06-24 05:29:26 Time zone: Asia/Tokyo (JST, +0900) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: n/a
SElinuxの無効化
EC2のRHELはデフォルトでSElinuxが有効になっているようですが、現在はSElinux力がないので、実力がつくその日まで無効にしておきます。
こちらのサイトを参考に確認・変更します。
5.4. SELinux の有効化および無効化 - Red Hat Customer Portal
以下の設定ファイルを修正します。
/etc/selinux/config
の以下の箇所を disabled
に修正します。
SELINUX=enforcing ↓ SELINUX=disabled
設定変更後は、OSの再起動が必要なので再起動します。
以上で、サーバの基本設定は完了です。
RHEL7でRailsアプリのサーバを構築した【Railsアプリケーション・Unicorn のインストール・設定】
こちらは、
RHEL7でRailsアプリのサーバを構築した - takapiのブログ
の Railsアプリケーション・Unicorn のインストール・設定
に関する記事です。
Railsアプリケーションは完成している前提で進めます。
Railsアプリケーションをクローンする
github等からRailsアプリケーションをクローンします。
例: $ git clone --depth 1 https://github.com/takapi86/my-app.git
bundler のインストール
RubyのGem管理ツールである bundler
をインストールします。
gem install bundler
Node.jsのインストール
必要に応じて設定します。 必須ではないので、Node.jsを使用しない場合はスキップします。
$ sudo yum remove -y nodejs npm # 最新のバージョンをインストールしたいので、すでにインストールされている場合は削除します。 $ sudo rpm -i https://rpm.nodesource.com/pub_6.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm $ sudo yum -y install nodejs
sqliteのインストール
必要に応じて設定します。 必須ではないので、 sqlite を使用しない場合はスキップします。
$ sudo yum -y install sqlite $ sudo yum -y install sqlite-devel
Railsアプリケーションのproduction設定
アセットのプリコンパイル
$ bundle exec rake assets:precompile RAILS_ENV=production
SECRET_KEY_BASEの設定
Railsでは config/secrets.yml にCookieのなりすまし対策のため、SECRET_KEY_BASE をCookie暗号化/復号化をしているようです。
production環境の場合、SECRET_KEY_BASE を環境変数に設定してあげる必要があります。
$ bundle exec rake secret b1cb4a8933d20deaafb19e152217b7a53f784912ceb51dce6507e7336e671eef0136438d81604ca4c4c39cffc2ee93ba472b2d6cc597c0f4f3d39c05343dede1
動作確認のため、一時的にこの値を環境変数にセットします。
export SECRET_KEY_BASE=b1cb4a8933d20deaafb19e152217b7a53f784912ceb51dce6507e7336e671eef0136438d81604ca4c4c39cffc2ee93ba472b2d6cc597c0f4f3d39c05343dede1
各Gemのインストール
bunlde install しGemのインストールします。
$ cd ~/my-app/ $ bundle install
DB設定
$ vi config/database.yml
production: adapter: mysql2 database: [DB名を入力します] pool: 5 username: [DBのユーザを入力します] password: [DBのパスワードを入力します] host: localhost # 今回はDBサーバも同じサーバなので、localhostをしています。
DBマイグレーション
以下のコマンドで、アプリケーションのDB、テーブル、データを作成します。
RAILS_ENV=production bundle exec rake db:create RAILS_ENV=production bundle exec rake db:migrate RAILS_ENV=production bundle exec rake db:seed
Railsアプリケーションの動作確認
最終的に sustemdで起動・停止できるようにしますが、ひとまずunicornコマンドを実行し、 production
モードで起動できるか確認します。
unicornはデフォルトでは port8080 であがるので、8080宛にアクセスして動作確認を行います。
unicorn -E production
ここまで確認ができればRailsアプリケーション側の設定は完了です。
systemd の設定
設定したRailsアプリケーションをsystemdで起動・停止できるようにします。
config/unicorn.rb を編集
systemdからは unicornの設定ファイル config/unicorn.rb
の内容で起動・停止できるようにします。
必要に応じて、以下に設定を追加・削除するとよいでしょう。
# config/unicorn.rb app_path = '/home/ec2-user/my-app' worker_processes 2 listen "#{app_path}/tmp/sockets/unicorn.sock", :backlog => 64 listen 8080, :tcp_nopush => true pid "#{app_path}/tmp/pids/unicorn.pid" rails_root = File.expand_path('../../', __FILE__) ENV['BUNDLE_GEMFILE'] = rails_root + "/Gemfile" stderr_path "#{app_path}/log/unicorn.stderr.log" stdout_path "#{app_path}/log/unicorn.stdout.log" before_exec do |server| ENV['BUNDLE_GEMFILE'] = "#{ENV['RAILS_ROOT']}Gemfile" end
上記の設定でサーバがあがるかひとまず unicorn コマンドで確認してみます。
bundle exec unicorn -c config/unicorn.rb -E production
systemdの設定
/etc/systemd/system/unicorn.service
[Unit] Description = unicorn Rack application server Wants=mysqld.service After=mysqld.service [Service] Environment=RAILS_ENV=production Environment=SECRET_KEY_BASE=[SECRET_KEYを設定します。] PIDFile=/home/ec2-user/my-app/tmp/pids/unicorn.pid ExecStart=/home/ec2-user/.rbenv/shims/unicorn -c "/home/ec2-user/my-app/config/unicorn.rb" "/home/ec2-user/my-app/config.ru" -E production [Install] WantedBy = multi-user.target
自動起動の設定
$ sudo systemctl enable unicorn.service
以下のコマンドで登録できているか確認します。
$ sudo systemctl list-unit-files --type=service | grep unicorn unicorn.service enabled
起動できるか確認します。
$ sudo systemctl start unicorn.service
NginxからUnicornを呼び出す
/etc/nginx/conf.d/
以下のような設定ファイルを追加し、リバースプロキシできるようにします。
必要に応じて、以下に設定を追加・削除するとよいでしょう。
server { listen 80 default_server; server_name www.example.com; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; root /home/ec2-user/my-app/public; client_max_body_size 100m; error_page 404 /404.html; error_page 500 502 503 504 /500.html; try_files $uri/index.html $uri @unicorn; location @unicorn { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass http://localhost:8080; } }
nginxを再起動します。
$ sudo systemctl restart nginx
ページにアクセスして確認する
ブラウザなどでサーバのURLへアクセスし、システムのページなどが表示されていればOKです。
表示されない場合は、ネットワーク周りやファイアウォールなどの設定を見直すとよいでしょう。
RHEL7でRailsアプリのサーバを構築した【Nginxのインストール・設定】
こちらは、
RHEL7でRailsサーバを構築した - takapi86のブログ
の Nginxのインストール・設定
に関する記事です。
Nginxのインストール・設定
Stable version(nginx-1.12.0)をインストールしていきます。
Nginxのyumリポジトリの追加
$ sudo rpm -i http://nginx.org/packages/rhel/7/noarch/RPMS/nginx-release-rhel-7-0.el7.ngx.noarch.rpm
Nginxのインストール
$ sudo yum -y install nginx
自動起動の設定
sudo systemctl enable nginx.service
nginx を起動する
$ sudo systemctl start nginx.service
デフォルトページにアクセスして確認する
サーバのURLへアクセスし、Nginxのデフォルトページが表示されていればOKです。
表示されない場合は、ネットワーク周りやファイアウォールなどの設定を見直すとよいでしょう。
以上で、nginxの設定は完了です。