TL;DR
- golangでWebアプリケーションを開発するにあたり,SAML認証を組み込む方法を紹介します
- Keycloakの機能を利用します
-
net/http
で作るWebアプリケーションに,認証を付けるサンプルを紹介します
-
crewjam/samlライブラリを使います
- Getting Started as a Service Providerのプログラムを参考に進めます
- 最も単純な構造のWebアプリケーションを実装します
準備
環境想定
- Webアプリケーションは,以下のような,超シンプルなものをつくります
- URLにリクエストを発行すると,ログインが求められます
- ログインするとユーザ名が表示されます
- テスト環境なので,Webアプリケーションはローカルホストで稼働させます
- keycloakへ認証の問い合わせを行うのでネットワークに接続されている必要があります
必要環境
- go 1.12.5
go version
go version go1.12.5 darwin/amd64
go get github.com/crewjam/saml/samlsp
-
Keyclockサーバ
- 各種サービスと連携するSSOサービスです
- 「OSSなシングルサインオンサービスKeycloakをdockerで立ち上げる」で立ち上げ方法を紹介しています
- gitlabおよびgrowiとの連携方法の記事もあるので,参考にしてください
-
バージョンは多少前後しても動くと思います
Keycloak設定
- Keycloakに管理者権限でログインし,連携したいRealmを選択します
- "Clients"から"Create"を選択します
- ClientIDを
http://localhost:8000/saml/metadata
のように,サービスを稼働させるURLを設定します - Client Protocolを"saml"でClientを作成します.
- Clientの各タブのうち,サンプルの段階で設定を要するのは以下のとおりです
- ClientIDを
Settingsタブ
フィールド名 | 値 |
---|---|
Client Signature Required | OFF |
Root URL | http://localhost:8000 |
Mappersタブ
- usernameだけ設定します
- usernameをマップします
Name | Mapper Type | Property | Friendly Name | SAML Attribute Name | SAML Attribute NameFormat |
---|---|---|---|---|---|
username | username | username | username | Basic |
SAML Keysタブ:x.509証明書の取得
-
ClientのSAML Keysタブから,Signing Keyを2つのファイルにコピーします
-
各ファイルには,下記のように接頭行,末尾行を追加します
- myservice.key
-
-----BEGIN PRIVATE KEY----- MII.....<snip>..... -----END PRIVATE KEY-----
- myservice.cert
-
-----BEGIN CERTIFICATE----- MII.....<snip>..... -----END CERTIFICATE-----
プログラム
- GitHub crewjam/samlのGetting Started as a Service Providerのプログラムを参考に進めます
- 以下のようなプログラム
main.go
を作成します-
main
メソッドの最初にある5行で,SAML設定に要する文字列を設定しています -
confIDPMetadataURL
: メタデータのURIを記載します.Keycloakの場合は,Keycloakのドメインに,realms/<Realm>/protocol/saml/descriptor
のパスを加えたものです- 下記コードでは例としてKeycloak(
https://sso.eample.com
), Realm(dev
)の場合を記載しています https://sso.example.com/auth/realms/dev/protocol/saml/descriptor
- 下記コードでは例としてKeycloak(
-
x509cert
,x509key
: 前項で作った証明書ファイルです.main.goと同じディレクトリに配置して,プログラムで読み込みます -
confRootURL
,confPort
: Webアプリケーションの設定です.KeycloakのClientIDにはconfRootURLと同じ文字列が設定されている必要があります.
-
//main.go
package main
import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"net/url"
"github.com/crewjam/saml/samlsp"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Printf("%+v", samlsp.Token(r.Context()))
fmt.Fprintf(w, "Hello, %s!", samlsp.Token(r.Context()).Attributes.Get("username"))
}
func main() {
confIDPMetadataURL := "https://sso.example.com/auth/realms/dev/protocol/saml/descriptor"
x509cert := "myservice.cert"
x509key := "myservice.key"
confRootURL := "http://localhost:8000"
confPort := ":8000"
keyPair, err := tls.LoadX509KeyPair(x509cert, x509key)
if err != nil {
panic(err) // TODO handle error
}
keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
if err != nil {
panic(err) // TODO handle error
}
idpMetadataURL, err := url.Parse(confIDPMetadataURI)
if err != nil {
panic(err) // TODO handle error
}
rootURL, err := url.Parse(confRootURL)
if err != nil {
panic(err) // TODO handle error
}
samlSP, _ := samlsp.New(samlsp.Options{
URL: *rootURL,
Key: keyPair.PrivateKey.(*rsa.PrivateKey),
Certificate: keyPair.Leaf,
IDPMetadataURL: idpMetadataURL,
})
app := http.HandlerFunc(hello)
http.Handle("/hello", samlSP.RequireAccount(app))
http.Handle("/saml/", samlSP)
http.ListenAndServe(confPort, nil)
}
動作確認
Webアプリの実行
- 作成したmain.go, myservice.key, myservice.confを同じディレクトリに置いて,
go run
コマンドで実行します-
go run main.go
-
ブラウザで確認
- Webアプリケーション
http://localhost:8000/hello
にアクセスすると,Keycloakのログイン画面に遷移します - Realmに登録されたユーザでログインすると,Webアプリケーションの画面に戻り,ユーザ名を使用した表示が行われます