Edited at

GolangでXMLHttpRequestLevel2+CORSのプリフライトが通るサーバーを立てる(BasicAuth付き)

More than 3 years have passed since last update.


GolangでCORS対応のHTTPサーバーを立てる


CORSとは

ブラウザでスクリプトからXMLHttpRequest(以下XHR)などで外部サイトのリソースにアクセスする場合、セキュリティ上の問題からよく知られた同一生成元ポリシーが適用されます。

元々のXHRでは読み込み元と同一のドメインでないかぎり直接のアクセスはできず、JSONPなどを利用する必要がありました。

それでは不便なので、XMLHttpRequestLevel2ではCORSというルールを規定し、外部ドメインからアクセスを許可しています。

詳しくはMozillaのこちら


CORSの動作


  • HTTPメソッドがGET, POST, HEADのいずれか

  • HTTPヘッダにAccept, Accept-Language, Content-Language, Content-Type以外のフィールドが含まれない

  • Content-Typeの値はapplication/x-www-form-urlencoded, multipart/form-data, text/plainのいずれか

以上の条件にすべて合致しない場合、

ブラウザは安全にアクセスできるかどうか、事前にOPTIONSでプリフライトを送信し確かめます。

プリフライトのレスポンスが適正な場合のみ、実際のリクエストを行います。


具体的にサーバーはなにをレスポンスするか


  • Access-Control-Allow-Originヘッダ

アクセスを許可するドメインです。

*と指定するとすべてのドメインからアクセス可能ですが、認証が必要な場合は指定できません(今回がこれです)


  • Access-Control-Allow-Headers

ブラウザが使用を許可されるリクエストヘッダです。

実際のリクエストで使われるリクエストヘッダがこれに含まれない場合、ブラウザはプリフライト失敗とみなします。


  • Access-Control-Allow-Methods

ブラウザが使用を許可されるメソッドです。

実際のリクエストで使われるメソッドがこれに含まれない場合、ブラウザはプリフライト失敗とみなします。


  • Access-Control-Allow-Credentials

認証を使用するかの可否です。

trueの場合、Basic認証などでのアクセスが可能です。

このヘッダをレスポンスできればCORS対応のサーバーになります


Golangでプリフライトをレスポンスする

以上をふまえて、こうします。

必ずBasic認証を使う場合になります。

import (

"net/http"
)

func routeHandler(w http.ResponseWriter, r *http.Request) {

実際にはここで各ヘッダのチェックが必要です

//リモートアドレスからのアクセスを許可する
w.Header().Set( "Access-Control-Allow-Origin", r.RemoteAddr )
//認証を行う
w.Header().Set( "Access-Control-Allow-Credentials", "true" )
w.Header().Set( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization" )
//必要なメソッドを許可する
w.Header().Set( "Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS" )

//XMLHttpRequest Level2のpreflightをチェック
if r.Method == "OPTIONS" {
//ヘッダーにAuthorizationが含まれていた場合はpreflight成功
s := r.Header.Get("Access-Control-Request-Headers")
if strings.Contains(s, "authorization") == true || strings.Contains(s, "Authorization") == true {
w.WriteHeader(204)
}
w.WriteHeader(400)
return
}
...以下通常のレスポンス
}

func main() {
http.HandleFunc(パス, routeHandler)
http.ListenAndServe(ポート, nil)
}