SPIRALのフォームでパスワードを変更する際に、新しいパスワードが現在のパスワードと重複していないかをチェックするサンプルプログラムを紹介します。
SPIRAL APIのレコード認証機能を利用することで、サーバーサイド(PHP)で安全にチェックを行うことができます。
これにより、ユーザーが同じパスワードを使い回すことを防ぎ、セキュリティを向上させることが可能です。
注意点
・ サンプルコードは、SPIRALのフォーム(更新)のPHPスクリプト設定で利用することを前提としています。
実装の概要
今回のコードでは、以下の流れで処理を行います。
パスワード更新フォームの確認画面で動作させ、入力されたパスワードが既存のものと一致する場合にエラーを返す仕組みです。
1. 更新フォームの確認画面で、入力された新しいパスワードとログイン中のユーザーIDを取得します。
2. SPIRAL APIのエンドポイント `/records/verify` を利用して、ユーザーIDとパスワードが一致するかを検証します。
3. 検証結果が「一致する(verified: true)」場合、それは「現在のパスワードと重複している」ことを意味します。
4. 検証結果をTH変数に格納し、フォームのHTML側で条件分岐させ、エラーメッセージなどを表示します。
事前準備
1. ログインID、パスワードなどを格納するためのDBを作成します。
2. 作成したDBに対して、パスワード変更用の更新フォームを作成します。フォームにはパスワード入力フィールドを設置してください。
3. 認証エリアにページを作成し更新フォームを設置した後、更新フォームのブロックのPHPタブに、後述するPHPコードを設置します。
PHP
更新フォームの確認画面(step=2)で動作するように、以下のコードをPHPに貼り付けます。
コピー
<?php
//------------------------------
// 設定値
//------------------------------
define("API_URL", "https://api.spiral-platform.com/v1");
define("API_KEY", "");
define("APP_ROLE", "");
define("APP_ID", "");
define("DB_ID", "");
define("LOGIN_ID_FIELD_ID", "1");
define("LOGIN_ID_FIELD_NAME", "mail");
define("LOGIN_PASSWORD_FIELD_NAME", "pass");
define("UPDATE_FORM_NAME","passchange");
//------------------------------
// API実行
//------------------------------
$SPIRAL->setTHValue("resultRecordVerify",false);
$updateForm = $SPIRAL->getUpdateForm(UPDATE_FORM_NAME);
$step = $updateForm->getStep();
$SPIRAL->setTHValue("resultRecordVerify",$step);
if($step == "2"){
$password = $SPIRAL->getParam(LOGIN_PASSWORD_FIELD_NAME);
$loginId = $SPIRAL->getAuthRecordByFieldId(LOGIN_ID_FIELD_ID);
$commonBase = CommonBase::getInstance();
// 検証したいレコードのIDとパスワード指定
$verifyData = array(
"idField" => array(LOGIN_ID_FIELD_NAME => $loginId),
"passwordField" => array(LOGIN_PASSWORD_FIELD_NAME => $password)
);
$resultRecordVerify = $commonBase->apiCurlAction("POST", "/apps/". APP_ID. "/dbs/". DB_ID. "/records/verify", $verifyData);
$SPIRAL->setTHValue("resultRecordVerify",$resultRecordVerify["verified"]);
}
//------------------------------
// 共通モジュール
//------------------------------
class CommonBase {
/**
* シングルトンインスタンス
* @var UserManager
*/
protected static $singleton;
public function __construct() {
if (self::$singleton) {
throw new Exception('must be singleton');
}
self::$singleton = $this;
}
/**
* シングルトンインスタンスを返す
* @return UserManager
*/
public static function getInstance() {
if (!self::$singleton) {
return new CommonBase();
} else {
return self::$singleton;
}
}
/**
* V2用 API送信ロジック
* @return Result
*/
function apiCurlAction($method, $addUrlPass, $data = null, $multiPart = null, $jsonDecode = null) {
$header = array(
"Authorization:Bearer ". API_KEY,
"X-Spiral-Api-Version: 1.1",
);
if($multiPart) {
$header = array_merge($header, array($multiPart));
} else {
$header = array_merge($header, array("Content-Type:application/json"));
}
if(APP_ROLE){
$header = array_merge($header, array("X-Spiral-App-Role: ".APP_ROLE));
}
// curl
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_URL, API_URL. $addUrlPass);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
if ($method == "POST") {
if ($multiPart) {
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
} else {
curl_setopt($curl, CURLOPT_POSTFIELDS , json_encode($data));
}
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
if ($method == "PATCH") {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
if ($method == "DELETE") {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
$response = curl_exec($curl);
if (curl_errno($curl)) echo curl_error($curl);
curl_close($curl);
if($jsonDecode){
return $response;
}else{
return json_decode($response, true);
}
}
}
?>
LOGIN_ID_FIELD_IDには、ログインIDに指定しているフィールドID
LOGIN_ID_FIELD_NAMEには、ログインIDのフィールドの識別名
LOGIN_PASSWORD_FIELD_NAMEには、パスワードフィールドの識別名を指定してください。
UPDATE_FORM_NAMEには、更新フォームの識別名を指定してください。
フォームHTMLでの利用方法
PHP内で$SPIRAL->setTHValue("resultRecordVerify", true); のようにTH変数をセットしました。フォームのHTML側では、この
resultRecordVerifyを使ってエラーメッセージの表示を制御します。
ブロック内のstep2のHTMLを以下のように変更し、エラーメッセージの分岐をしてください。 コピー
<div class="sp-form-container">
<!-- パスワード変更されていない場合のメッセージ表示 -->
<div th:if="${cp.result.value['resultRecordVerify'] == true}">
<p style="font-size: 18pt; color: red;">パスワードが現在のパスワードから変更されていません</p>
<div class="sp-form-item sp-form-interaction">
<button class="sp-form-prev-button" type="submit" name="action" value="previous"
th:if="!${step.isFirst}" th:text="${step.prevButtonLabel}">Prev</button>
</div>
</div>
<!-- 通常のフォーム表示(変更された場合のみ表示) -->
<div th:unless="${cp.result.value['resultRecordVerify'] == true}">
<div class="sp-form-item sp-form-html" th:inline="none">
<p><span style="font-size: 18pt;">パス変更</span></p>
</div>
<!-- パスワード(pass) -->
<div class="sp-form-item sp-form-field">
<div class="sp-form-label" th:text="${fields['f02'].label}">
Label
</div>
<div class="sp-form-data">
<span class="sp-form-embedded" th:text="${values['f02']?.mask()}">****************</span>
</div>
</div>
<div class="sp-form-item sp-form-interaction">
<button class="sp-form-prev-button" type="submit" name="action" value="previous"
th:if="!${step.isFirst}" th:text="${step.prevButtonLabel}">Prev</button>
<button class="sp-form-next-button" type="submit" name="action" value="next"
th:text="${step.nextButtonLabel}">Next</button>
</div>
</div>
</div>
まとめ
サーバーサイドでチェックを行うことで、よりセキュアなパスワード管理が実現できます。
このサンプルを参考に、ご自身の運用に合わせたカスタマイズを試してみてください。