##GolangでCORS対応のHTTPサーバーを立てる
###CORSとは
ブラウザでスクリプトからXMLHttpRequest(以下XHR)などで外部サイトのリソースにアクセスする場合、セキュリティ上の問題からよく知られた同一生成元ポリシーが適用されます。
元々のXHRでは読み込み元と同一のドメインでないかぎり直接のアクセスはできず、JSONPなどを利用する必要がありました。
それでは不便なので、XMLHttpRequestLevel2ではCORSというルールを規定し、外部ドメインからアクセスを許可しています。
###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)
}