Index
前提
- ここではXAMPPを使っています、なのでデータベースはMySqlです。
- こちらの記事の環境構築をした後に本記事を書いています。
- SimpleSAMLphpはすでにダウンロード済でApache制御下のフォルダに配置済という想定です。
- SimpleSAMLphpのフォルダ名をssoIdpに変更して使用しており、以下のような構成になっています。
C:\xampp\htdocs (←Apacheの制御下のフォルダ、環境の設定によって変わる)
└ ssoIdp(←SimpleSAMLphpのサイトからダウンロードして解凍した
フォルダ[simplesamlphp-1.19.0-rc1]をリネームしています。)
MySqlにユーザアカウントを作成
- phpMyAdminのコンソール画面を開きます
XAMPPのコンソールからMySQLを「Start」した後、[Admin]ボタンをクリックしてphpMyAdminのコンソール画面を開きます。
- データベースを作成します
phpMyAdminにログイン(IDとパスワードを設定している場合は認証してログイン)して、左端カラムの[新規作成]をクリックします。
データベースの名前に「simplesaml」と入力し、[作成]ボタンをクリックします。
- テーブルを作成します
このページ(sqlauth:sql)記載のSQLを実行すればよいです。
データベース"simplesaml"の"SQL"タブでCREATE文を入力し、[実行]ボタンをクリックします。
データベース"simplesaml"に、"users"テーブルと"usergroups"テーブルが作成されました。
- PHPのプログラムからユーザを登録
"users"テーブルの"password"カラムの値は、『saltとpassword平文を結合した文字列にハッシュをかけたもの』です。なのでユーザの登録がちょっと面倒ですからPHPでさくっと登録用画面のプログラムを書きました、割と適当ですが。
<?php
class simpleSamlDb
{
//各人のMySqlの環境に合わせて設定してください。
const DATA_SOURCE_NAME = "mysql:dbname=simplesaml;host=localhost";
const DB_USER_NAME = "root";
const DB_PASSWORD = null;
private $dbh;
function __construct()
{
try {
$this->dbh = new PDO(self::DATA_SOURCE_NAME, self::DB_USER_NAME, self::DB_PASSWORD);
} catch (Exception $ex) {
var_export($ex->getMessage());
}
}
public function addUser($uid, $password, $givenname, $email)
{
$sql = "insert into users (uid, password, salt, givenname, email, eduPersonPrincipalName) "
. "values (:uid, SHA2(:password, 256), :salt, :givenname, :email, :eduPersonPrincipalName)";
try {
$salt = substr(bin2hex(random_bytes(32)), 0, 32); //ランダム文字列を生成
$this->dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $this->dbh->prepare($sql);
$stmt->bindValue(":uid", $uid, PDO::PARAM_STR);
$stmt->bindValue(":password", $salt . $password, PDO::PARAM_STR);
$stmt->bindValue(":salt", $salt, PDO::PARAM_STR);
$stmt->bindValue(":givenname", $givenname, PDO::PARAM_STR);
$stmt->bindValue(":email", $email, PDO::PARAM_STR);
$stmt->bindValue(":eduPersonPrincipalName", $email, PDO::PARAM_STR); //とりあえずemailと同じ値にしてます。eduPersonの仕様にはそんなに詳しくないので。
if($stmt->execute() == 1 ){
echo "登録しました";
};
} catch (PDOException $ex) {
echo "hopge";
var_export($ex->getMessage());
}
}
}
if (
array_key_exists("uid", $_POST) &&
array_key_exists("password", $_POST) &&
array_key_exists("givenname", $_POST) &&
array_key_exists("email", $_POST)
) {
$db = new simpleSamlDb();
$db->addUser($_POST["uid"], $_POST["password"], $_POST["givenname"], $_POST["email"]);
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SimpleSAMLphpのDBにユーザを追加したりする</title>
</head>
<body>
<form method="POST" action="#">
<label>uid</label><br />
<input id="uid" name="uid" value="testuser" /><br />
<label>password</label><br />
<input id="password" name="password" value="testpass" /><br />
<label>givenname</label><br />
<input id="givenname" name="givenname" value="testgivenname" /><br />
<label>email</label><br />
<input id="email" name="email" value="test@example.simplesaml.com" /><br />
<input type="submit" id="submit" value="送信" />
</form>
</body>
</html>
このPHPのソースコードをWebブラウザで表示するとこんな感じになります。入力欄に値を設定して[送信]ボタンをクリックすればMySqlにユーザが登録されます。
ここでは(ユーザ名:testuser、パスワード:testpass)で登録しています。
Webブラウザから登録したユーザをphpMyAdminのコンソールから確認しました。ちゃんと登録されてますね。
IdPの設定
- 設定ファイル「ssoIdp\config\config.php」を編集します。
URLのパスを変更します。
'baseurlpath' => 'simplesaml/',
↓↓↓
'baseurlpath' => 'ssoIdp/',
アドミニストレータのパスワードも変更します。(初期設定の値から変更しておかないとログインできないので。)
'auth.adminpassword' => '123',
↓↓↓
'auth.adminpassword' => '123456',
IdPでのsaml2.0の機能を有効化します。
'enable.saml20-idp' => false,
↓↓↓
'enable.saml20-idp' => true,
sqlauthを有効化します。
「 ssoIdp\config\config.php 」のmocule.enableに「'sqlauth' => true」を追加します。
'module.enable' => [
'exampleauth' => false,
'sqlauth' => true,
'core' => true,
'saml' => true
],
- ssoIdp/config/authsources.phpの設定
MySqlに接続して、認証情報取得のために実行するSQLクエリを設定を追加します。(※'dsn','username','password'は各自の環境に合わせて設定してください。 )
/*
'example-sql' => [
'sqlauth:SQL',
'dsn' => 'pgsql:host=sql.example.org;port=5432;dbname=simplesaml',
'username' => 'simplesaml',
'password' => 'secretpassword',
'query' => 'SELECT uid, givenName, email, eduPersonPrincipalName FROM users WHERE uid = :username ' .
'AND password = SHA2(CONCAT((SELECT salt FROM users WHERE uid = :username), :password), 256);',
],
*/
↓↓↓
'example-sql' => [
'sqlauth:SQL',
'dsn' => 'mysql:host=localhost;port=3306;dbname=simplesaml',
'username' => 'root',
'password' => '',
'query' => 'SELECT uid, givenName, email, eduPersonPrincipalName FROM users WHERE uid = :username ' .
'AND password = SHA2(CONCAT((SELECT salt FROM users WHERE uid = :username), :password), 256);'
]
- IdPサーバのメタデータを編集します
使用する認証ソースを'example-userpass'から'example-sql'に変更します。
/*
* Authentication source to use. Must be one that is configured in
* 'config/authsources.php'.
*/
'auth' => 'example-userpass',
↓↓↓
'auth' => 'example-sql',
- 鍵ペアを ssoIdp\cert 配下に配置します。
opensslなどを使って鍵ペアを生成すればいいです。
一応、こちらのページにサンプルの鍵ペア(秘密鍵をを公開しているので実用には耐えない)の中身を用意してあるので、そちらを使ってもらってもかまいません。
こんな感じのファイル構成になります。
C:\xampp\htdocs (←Apacheの制御下のフォルダ、環境の設定によって変わる)
└ ssoIdp
└ cert
├ server.pem
└ server.crt
【補足】
ssoIdp\metadata\saml20-idp-hosted.php にて以下のように秘密鍵と証明書の設定をしているのですが、秘密鍵と証明書のファイル名を同じにしているので、こちらの設定を変更する必要はないです。
// X.509 key and certificate. Relative to the cert directory.
'privatekey' => 'server.pem',
'certificate' => 'server.crt',
- Apacheの設定
<Directory "C:/xampp/htdocs">の下あたりにでも以下のように設定を追加します。
DocumentRoot "C:/xampp/htdocs"
<Directory "C:/xampp/htdocs">
~省略~
</Directory>
<Directory "C:\xampp\htdocs\ssoIdp\www">
Allow from localhost
</Directory>
<Directory "C:\xampp\htdocs\ssoIdp">
Deny from all
</Directory>
Alias /ssoIdp/ "C:/xampp/htdocs/ssoIdp/www/"
- MySqlに登録したユーザアカウントを用いて認証テストをしてみます
WEBブラウザで表示した設定画面の[認証]タブの「設定されている認証元をテスト」をクリックします。
先程MySqlに追加したユーザアカウントのIDとパスワード(ユーザ名:testuser、 パスワード : testpass)を入力し、[ログイン]をクリックしてください。ちなみにすでにIdPでの認証が済んでいる場合は、この画面はスキップされます。
認証に成功すると以下のような画面が表示されます。"属性"の項を見てみると、config.phpで設定したSQL (SELECT uid, givenName, email, eduPersonPrincipalName FROM users ....)によりMySqlからパラメータを取得できていることがわかります。
SPとSAML連携してみる
※SPサーバの構築の仕方についてはこちらを参考にしてください。
- SPサーバの設定画面>[認証]タブ>"設定されている認証元をテスト">"example-sql">プルダウンでIdPを[選択]して、SPからSAMLRequestをIdPに飛ばす。
- IdPサーバの認証画面へ遷移するので、先程MySqlに追加したユーザアカウントのIDとパスワード(ユーザ名:testuser、 パスワード : testpass)を入力し、[ログイン]をクリックしてください。
- IdPでの認証に成功すると、IdPからSAMLResponseがSPに送信され、それの検証によりSPでの認証が完了します。下の画像はSPでの認証に成功したときのものです。
SAMLResponseにパラメータを含ませる
先に書いちゃいますけど、SAMLの仕様によりSAMLResponse内のAttributeStatement要素に諸々の属性を追加できます。
下の例だと、上から順に"uid","givenName","email","eduPersonPrincipalName","groups"のパラメータが含まれています。
"uid"の値によってSP側で利用ユーザを特定できるのは当然として、"groups"はユーザが属しているグループを意味していますから、それによってSP側で利用可能な機能や権限を管理するといった使い方もできます。この例のユーザだとpopとrockという2つのグループに属しています。
<saml:AttributeStatement>
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">testuser</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="givenName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">testgivenname</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test@example.simplesaml.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="eduPersonPrincipalName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test@example.simplesaml.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">pop</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string">rock</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
- では、ユーザとグループを紐づけてみましょう。phpMyAdminのコンソールから"usergroups"テーブルに"testuser"に紐づくグループのデータを挿入します。ここではrockとpopというグループに"testuser"を紐づけています。値を入力して[実行]ボタンをクリックすればいいです。
ちなみにSQLだとこのようなクエリになります。
INSERT INTO `usergroups` (`uid`, `groupname`) VALUES ('testuser', 'rock'), ('testuser', 'pop');
'example-sql' => [
'sqlauth:SQL',
'dsn' => 'mysql:host=localhost;port=3306;dbname=simplesaml',
'username' => 'root',
'password' => '',
'query' => 'SELECT users.uid, givenName, email, eduPersonPrincipalName, groupname AS groups ' .
'FROM users LEFT JOIN usergroups ON users.uid = usergroups.uid ' .
'WHERE users.uid = :username AND password = SHA2(CONCAT((SELECT salt FROM users WHERE uid = :username), :password), 256);'
],
このページ(sqlauth:sql)記載のSQLを少し変更したものです、"usergroups"テーブルをJOINしてSELECTするSQLに変更してます。
(このSQLを実行すると複数行を返しますが、その場合は属性単位で纏める仕様らしいです。)
- 以下は実際にSAML連携をしてみたときのSPの画面です。groupsの情報がIdPからSPに伝えられてますね。
【備考】
このようなIdPから受け取ったパラメータをSPで利用することにより、Just-In-Time(JIT)プロビジョニング連携をすることもできます。
JIT連携とは‥‥一般的なSSOサービスを利用する場合、事前に同じユーザ情報をIdPとSPの両方で登録しておく必要があります。しかし、IdPからのSAMLResponseをSPが受け取ったタイミング(Just-In-Time)でユーザを新規登録することにより、事前のユーザ登録をすることなくSSOを利用できる仕組みです。