MFCで物品購入承認システム作成 6

前回までの話。

MFCで物品購入承認システム作成 1

MFCで物品購入承認システム作成 2

MFCで物品購入承認システム作成 3

MFCで物品購入承認システム作成 4

MFCで物品購入承認システム作成 5

ユーザ情報変更ダイアログ作成

今回はユーザ情報変更ダイアログ作成を。
早い話、ユーザテーブルにINSERT、UPDATE、DELETEを行う処理です。

いつも通り、リソースビューからDialogを右クリック、
リソースの追加を選択し、新規作成します。

ダイアログIDはIDD_DIALOG_USERINFO_CHGにしておきます。

必要なコントロールは
ラジオボタン3つ、スタティック5つ、エディットボックス4つとコンボボックス1つ。
デフォルトで存在するOKボタンとキャンセルボタンは今回活用します。

早速配置。

今回は特筆することはありません。
強いて言えば、コンボボックスはドロップダウンリストにするくらい?
スタティックテキストや「OK」は、ラジオボタンの選択によって変更するので、
一時的な文字を入れています。

各コントロールのIDと配置はこの通り。

IDD_DIALOG_USERINFO_CHG DIALOGEX 0, 0, 160, 240
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,20,220,50,14
    PUSHBUTTON      "キャンセル",IDCANCEL,80,220,50,14
    CONTROL         "作成",IDC_RADIO_INSERT,"Button",BS_AUTORADIOBUTTON,20,7,30,10
    CONTROL         "変更",IDC_RADIO_UPDATE,"Button",BS_AUTORADIOBUTTON,60,7,30,10
    CONTROL         "削除",IDC_RADIO_DELETE,"Button",BS_AUTORADIOBUTTON,100,7,30,10
    LTEXT           "ユーザID",IDC_STATIC_USERINFO_ID,20,30,70,8
    EDITTEXT        IDC_EDIT_USERINFO_ID,20,45,100,14,ES_AUTOHSCROLL | ES_NUMBER
    LTEXT           "ユーザ名",IDC_STATIC_USERINFO_NAME,20,65,70,8
    EDITTEXT        IDC_EDIT_USERINFO_NAME,20,80,100,14,ES_AUTOHSCROLL
    LTEXT           "パスワード",IDC_STATIC_USERINFO_PASS,20,100,70,8
    EDITTEXT        IDC_EDIT_USERINFO_PASS,20,115,100,14,ES_PASSWORD | ES_AUTOHSCROLL
    LTEXT           "パスワード確認",IDC_STATIC_USERINFO_PASS2,20,135,70,8
    EDITTEXT        IDC_EDIT_USERINFO_PASS2,20,150,100,14,ES_PASSWORD | ES_AUTOHSCROLL
    LTEXT           "ユーザ権限",IDC_STATIC_USERINFO_AUTH,20,170,70,8
    COMBOBOX        IDC_COMBO_USERINFO_AUTH,20,185,100,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
END

ログインダイアログ作成時にも行ったように、
IDエディットボックスには「数字」をTrue、
パスワードエディットボックス2つには「パスワード」をTrueにします。

コントロールをダブルクリックして、クラスを追加します。
クラス名、cpp名、h名は以下のように決定。

作成されると、忘れないうちにクラスビューからCUserInfoChgを選択し、
プロパティのオーバーライドからOnInitDialogをAddしておきます。

今回コントロールクリック時に処理を走らせたいのは
OKボタンとラジオボタン3つ。
リストビューのダイアログからそれぞれダブルクリックし、関数を自動追加しておきます。

なのでUserInfoChg.hには以下のような関数が宣言されているはずです。

public:
	virtual BOOL OnInitDialog();
	afx_msg void OnBnClickedOk();			// OKボタン
	afx_msg void OnBnClickedRadioInsert();	// 作成ラジオボタン
	afx_msg void OnBnClickedRadioUpdate();	// 変更ラジオボタン
	afx_msg void OnBnClickedRadioDelete();	// 削除ラジオボタン

ここで一瞬、MySQLManager.hを開いて、enumを作っておきます。

#pragma once
#include <mysql/jdbc.h>

// SQL実行モード
typedef enum
{
	enInsert,	// 作成
	enUpdate,	// 変更
	enDelete	// 削除
}SQLMode;

class CMySQLManager
{
//略

ダイアログレイアウト設定

はい、またUserInfoChg.hに戻ります。

まず最初にヘッダーインクルード。

#include "MySQLManager.h"

続いてprivateメンバ変数や関数を用意します。

private:
	CString m_strUserID;			// ユーザID
	CString m_strUserName;			// ユーザ名
	CString m_strUserPassword;		// ユーザパスワード
	CString m_strUserPassword2;		// ユーザパスワード確認
	SQLMode m_enSQLMode;			// SQL実行モード

	CEdit	m_ctrlEditID;			// ユーザIDエディットボックス
	CEdit	m_ctrlEditName;			// ユーザ名エディットボックス
	CEdit	m_ctrlEditPassword;		// ユーザパスワードエディットボックス
	CEdit	m_ctrlEditPassword2;	// ユーザパスワード確認エディットボックス
	CComboBox m_ctrlComboAuth;		// ユーザ権限コンボボックス

	CStatic m_ctrlStaticID;			// ユーザIDスタティックテキスト
	CStatic m_ctrlStaticName;		// ユーザ名スタティックテキスト
	CStatic m_ctrlStaticPassword;	// ユーザパスワードスタティックテキスト
	CStatic m_ctrlStaticPassword;	// ユーザパスワード確認スタティックテキスト
	CStatic m_ctrlStaticAuth;		// ユーザ権限スタティックテキスト

private:
	void RadioConditionChange(SQLMode enMode);	// ラジオボタン変更時
	BOOL bValidCheck();							// 入力チェック

エディットボックスは、ラジオボタンの状況に応じて入力の可否を決定させます。

具体的な処理を書きます。
UserInfoChg.cppを開きます。

まず、コンストラクタにはメンバ変数初期化。
DoDataExchangeにはIDとメンバ変数を紐づけます。

CUserInfoChg::CUserInfoChg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_USERINFO_CHG, pParent)
{
	m_strUserID = _T("");
	m_strUserName = _T("");
	m_strUserPassword = _T("");
	m_strUserPassword2 = _T("");
	m_enSQLMode = enInsert;
}

void CUserInfoChg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_USERINFO_ID, m_strUserID);
	DDV_MaxChars(pDX, m_strUserID, 5);
	DDX_Text(pDX, IDC_EDIT_USERINFO_NAME, m_strUserName);
	DDX_Text(pDX, IDC_EDIT_USERINFO_PASS, m_strUserPassword);
	DDX_Text(pDX, IDC_EDIT_USERINFO_PASS2, m_strUserPassword2);

	DDX_Control(pDX, IDC_EDIT_USERINFO_ID, m_ctrlEditID);
	DDX_Control(pDX, IDC_EDIT_USERINFO_NAME, m_ctrlEditName);
	DDX_Control(pDX, IDC_EDIT_USERINFO_PASS, m_ctrlEditPassword);
	DDX_Control(pDX, IDC_EDIT_USERINFO_PASS2, m_ctrlEditPassword2);
	DDX_Control(pDX, IDC_COMBO_USERINFO_AUTH, m_ctrlComboAuth);

	DDX_Control(pDX, IDC_STATIC_USERINFO_ID, m_ctrlStaticID);
	DDX_Control(pDX, IDC_STATIC_USERINFO_NAME, m_ctrlStaticName);
	DDX_Control(pDX, IDC_STATIC_USERINFO_PASS, m_ctrlStaticPassword);
	DDX_Control(pDX, IDC_STATIC_USERINFO_PASS2, m_ctrlStaticPassword2);
	DDX_Control(pDX, IDC_STATIC_USERINFO_AUTH, m_ctrlStaticAuth);
}

OnInitDialogに、初期処理を記述。

BOOL CUserInfoChg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO: ここに初期化を追加してください
	//コンボボックス設定
	m_ctrlComboAuth.AddString(_T("0:なし(一般ユーザ)"));
	m_ctrlComboAuth.AddString(_T("1:あり(管理者ユーザ)"));
	// 権限なしに設定
	m_ctrlComboAuth.SetCurSel(0);

	//ラジオボタン「作成」にセット
	CButton* pButton = (CButton*)GetDlgItem(IDC_RADIO_INSERT);
	pButton->SetCheck(1);

	RadioConditionChange(enInsert);	//作成モードのコントロール状態

	SetWindowText(_T("ユーザ情報変更"));

	return TRUE;  // return TRUE unless you set the focus to a control
	// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}

7~10行目はコンボボックスの初期処理です。
14行目で「作成」ラジオボタンにセット。
16行目は、「作成」が選択された際のエディットボックスとコンボボックスの入力可否を決める関数です。後で処理は書きます。

次に、ラジオボタンが押された際に呼ばれる関数(OnBnClickedRadio○○)に
それぞれ処理を書きます。
とは言っても、共通処理RadioConditionChangeに飛ばすだけですが。

/**
 * @fn
 * @brief	「作成」ラジオボタン押下時に呼ばれる
 * @param	なし
 * @return	なし
 */
void CUserInfoChg::OnBnClickedRadioInsert()
{
	m_enSQLMode = enInsert;
	RadioConditionChange(enInsert);
}

/**
 * @fn
 * @brief	「変更」ラジオボタン押下時に呼ばれる
 * @param	なし
 * @return	なし
 */
void CUserInfoChg::OnBnClickedRadioUpdate()
{
	m_enSQLMode = enUpdate;
	RadioConditionChange(enUpdate);
}

/**
 * @fn
 * @brief	「削除」ラジオボタン押下時に呼ばれる
 * @param	なし
 * @return	なし
 */
void CUserInfoChg::OnBnClickedRadioDelete()
{
	m_enSQLMode = enDelete;
	RadioConditionChange(enDelete);
}

続いて、ラジオボタンが変更された際のコントロールの状態を決める関数の処理を。

何をするかですが、

  • 「作成」時
    →すべてのエディットボックスが入力可能、コンボボックスは選択可能。
     OKボタンは「作成」ボタン、スタティックも「新規○○」に変更。
  • 「変更」時
    →すべてのエディットボックスが入力可能、コンボボックスは選択可能。
     OKボタンは「変更」ボタン、スタティックも「変更○○」に変更。
  • 「削除」時
    →IDエディットボックスのみ入力可能。
     OKボタンは「削除」ボタン、スタティックも「削除○○」に変更。
/**
 * @fn
 * @brief	ラジオボタン変更処理
 * @param	なし
 * @return	なし
 */
void CUserInfoChg::RadioConditionChange()
{
	// 初期化
	m_strUserID = _T("");
	m_strUserName = _T("");
	m_strUserPassword = _T("");
	m_strUserPassword2 = _T("");
	m_ctrlComboAuth.SetCurSel(0);

	//入力可否
	if (m_enSQLMode == enInsert)
	{
		//「作成」時
		// スタティックテキスト変更
		m_ctrlStaticID.SetWindowText(_T("新規ユーザID"));
		m_ctrlStaticID.SetWindowText(_T("新規ユーザ名"));
		m_ctrlStaticID.SetWindowText(_T("新規ユーザパスワード"));
		m_ctrlStaticID.SetWindowText(_T("新規ユーザパスワード確認"));
		m_ctrlStaticID.SetWindowText(_T("新規ユーザ権限"));
		// エディット・コンボボックスはすべてアクティブ
		m_ctrlEditID.EnableWindow(TRUE);
		m_ctrlEditName.EnableWindow(TRUE);
		m_ctrlEditPassword.EnableWindow(TRUE);
		m_ctrlEditPassword2.EnableWindow(TRUE);
		m_ctrlComboAuth.EnableWindow(TRUE);
	}
	else if (m_enSQLMode == enUpdate)
	{
		//「変更」時
		// スタティックテキスト変更
		m_ctrlStaticID.SetWindowText(_T("変更ユーザID"));
		m_ctrlStaticID.SetWindowText(_T("変更ユーザ名"));
		m_ctrlStaticID.SetWindowText(_T("変更ユーザパスワード"));
		m_ctrlStaticID.SetWindowText(_T("変更ユーザパスワード確認"));
		m_ctrlStaticID.SetWindowText(_T("変更ユーザ権限"));
		// エディット・コンボボックスはすべてアクティブ
		m_ctrlEditID.EnableWindow(TRUE);
		m_ctrlEditName.EnableWindow(TRUE);
		m_ctrlEditPassword.EnableWindow(TRUE);
		m_ctrlEditPassword2.EnableWindow(TRUE);
		m_ctrlComboAuth.EnableWindow(TRUE);
	}
	else if (m_enSQLMode == enDelete)
	{
		//「削除」時
		// スタティックテキスト変更
		m_ctrlStaticID.SetWindowText(_T("削除ユーザID"));
		m_ctrlStaticID.SetWindowText(_T(""));
		m_ctrlStaticID.SetWindowText(_T(""));
		m_ctrlStaticID.SetWindowText(_T(""));
		m_ctrlStaticID.SetWindowText(_T(""));
		// エディットボックスはIDのみアクティブ
		m_ctrlEditID.EnableWindow(TRUE);
		m_ctrlEditName.EnableWindow(FALSE);
		m_ctrlEditPassword.EnableWindow(FALSE);
		m_ctrlEditPassword2.EnableWindow(FALSE);
		m_ctrlComboAuth.EnableWindow(FALSE);
	}

	UpdateData(FALSE);
}

ラジオボタンが変更されると、まずエディットボックスは初期状態に戻します。
コンボボックスも同様です。

その後、アクティブ状態を設定、ボタンキャプションも変更します。

ここまでできたので、一度動かしてみます。

リソースビューから前回作成したユーザリストダイアログを選択、
「ユーザ情報変更」ボタンをクリックして押下時に呼ばれる関数を作成します。

UserManage.cppを開き、ヘッダーをインクルードします。

#include "UserInfoChg.h"

先ほど追加した関数に処理を記述。

void CUserManage::OnBnClickedButtonUsermgChg()
{
	CUserInfoChg dlgUserInfoChg;
	if (dlgUserInfoChg.DoModal() == IDOK)
	{
		//処理を実施した場合はリスト再表示
		ListUpdate();
	}
}

これで、ユーザ情報変更ボタンを押すと今回作成したダイアログが出現します。

早速ビルド&実行。

ユーザ情報変更ボタンを押すと。

ほぼ想定通りです。
パスワード確認スタティックが見切れてますね。
これは後で直しておきます。

このまま適当に入力し、ラジオボタンを変更にセットすると

初期化されましたし、スタティックも変更されています。

削除の場合は

わかりにくいですが、消えてるわけではなく入力不可になっています。

パスワード確認のスタティックだけ変更します。
SupplyRequestManager.rcから。

    LTEXT           "パスワード確認",IDC_STATIC_USERINFO_PASS2,20,135,100,8

入力チェック

入力のチェック順としては

  1. アクティブコントロールのどれかが空欄ならNG (ダイアログ側)
  2. IDが5桁未満ならNG (ダイアログ側)
  3. パスワードが一致しなければNG (ダイアログ側)
  4. 入力確認 (ダイアログ側)
  5. INSERTの場合、IDが重複ならNG
    UPDATEとDELETEの場合、IDが存在しなければNG (SQL操作側)
  6. 上記問題なければSQL実行

1~4を作ります。

/**
 * @fn
 * @brief	入力チェック
 * @param	なし
 * @return	なし
 */
BOOL CUserInfoChg::bValidCheck()
{
	if (m_strUserID == _T(""))
	{
		//IDは全モードで
		AfxMessageBox(_T("未入力項目があります。"));
		return FALSE;
	}
	if (m_enSQLMode == enInsert || m_enSQLMode == enUpdate)
	{
		// 作成と変更は他もチェック
		if (m_strUserName == _T("") || m_strUserPassword == _T("") || m_strUserPassword2 == _T(""))
		{
			AfxMessageBox(_T("未入力項目があります。"));
			return FALSE;
		}
	}

	if (m_strUserID.GetLength() < 5)
	{
		// 入力IDが5桁未満の場合
		AfxMessageBox(_T("IDは5桁で入力してください。"));
		return FALSE;
	}

	if ( m_strUserPassword != m_strUserPassword2)
	{
		// パスワードが一致しない場合
		AfxMessageBox(_T("パスワードが一致しません。"));
		return FALSE;
	}


	CString strAuth = _T("権限なし");
	if (m_ctrlComboAuth.GetCurSel() == 1)
	{
		strAuth = _T("権限あり");
	}
	CString strMes = _T("");
	if (m_enSQLMode == enInsert || m_enSQLMode == enUpdate)
	{
		if (m_enSQLMode == enInsert)
		{
			strMes = _T("以下のユーザを登録します。よろしいですか?");
		}
		else
		{
			strMes = _T("以下のユーザを更新します。よろしいですか?");
		}
		strMes += _T("\r\nユーザID:");
		strMes += m_strUserID;
		strMes += _T("\r\nユーザ名:");
		strMes += m_strUserName;
		strMes += _T("\r\nパスワード:非表示");
		strMes += _T("\r\n管理者権限:");
		strMes += strAuth;
	}
	else
	{
		strMes = _T("以下のユーザを削除します。よろしいですか?");
		strMes += _T("\r\nユーザID:");
		strMes += m_strUserID;
	}

	//確認メッセージ
	if (AfxMessageBox(strMes, MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
	{
		return FALSE;
	}

	return TRUE;
}

そしてこのチェックはOKボタン(作成or変更or削除)を押されたタイミングで呼びます。
まず、UserInfoChg.cppにMySQLManagerのヘッダーをインクルード。

#include "MySQLManager.h"

OKボタン押下時の処理は以下のよう。

/**
 * @fn
 * @brief	「作成」ボタンor「変更」ボタンor「削除」ボタン押下時の処理
 * @param	なし
 * @return	なし
 */
void CUserInfoChg::OnBnClickedOk()
{
	UpdateData(TRUE);
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	if (bValidCheck() == TRUE)
	{
		//チェックに問題はなかった
		CMySQLManager UserInfo;
		UserInfo.GetConnection(); // 接続確立
		CString strUserAuth;
		strUserAuth.Format(_T("%d"), m_ctrlComboAuth.GetCurSel());  // 権限CString化
		if (UserInfo.bUserTableChange(m_enSQLMode, m_strUserID, m_strUserName, m_strUserPassword, strUserAuth) == TRUE)
		{
			AfxMessageBox(_T("処理が完了しました。\r\n自身の情報を変更した場合は一度ログアウトしてください。"));
			CDialog::OnOK();
		}
	}
}

まずbValidCheckで入力チェックを行います。
続いていつも通りインスタンス化、接続確立。
CMySQLManager側で処理が完了したらメッセージを出します。

本当は自分の情報を変更する場合は何かしらの制御が必要ですが、
今回は一度ログアウトしてもらうという妥協で。

15行目、こちらはまだ作成していないのでエラーになります。
これから作成です。

SQL実行

MySQLManager.hを開きます。
今回、2つ関数を宣言しておきます。

public:
	//ユーザ情報変更
	BOOL bUserTableChange(SQLMode enMode, CString strUserID, CString strUserName, CString strUserPassword, CString strUserAuth);

private:
	//ハッシュ化
	sql::SQLString strHashing(CString strPassword);

bUserTableChangeでは引数の情報をもとに、テーブルに変更を加えます。
そしてstrHashingはパスワードのハッシュ化です。

ハッシュ化関数

まず処理が簡単なstrHashingの方から。

そもそもですが、パスワードは生の状態でテーブルに残すのは
通常セキュリティ上ありえません。

一度ハッシュ化してテーブルに登録し、
ログイン時には入力されたパスワードをハッシュ化し、
テーブルに登録されているハッシュ済パスワードと比較します。

まぁここら辺はここで述べるよりもセキュリティ関連の勉強をすれば自然と通る道です。
ここで詳しく述べることはしません。

MySQLManager.cppを開きます。
ハッシュ化にもいろいろ方法はあります。

今回使うのは、標準ライブラリが提供する簡易的なハッシュ関数で、
特定のアルゴリズム(SHA-256など)には依存していません。

/**
 * @fn
 * @brief	パスワードハッシュ化
 * @param	CString       strPassword   変換前パスワード
 * @return	sql::SQLString              ハッシュ化後パスワード
 */
sql::SQLString CMySQLManager::strHashing(CString strPassword)
{
    CT2CA pszConvertedAnsiString(strPassword);
    std::string strStd(pszConvertedAnsiString);
    std::hash<std::string> hash_fn;

    std::string strValue = std::to_string(hash_fn(strStd)); //ハッシュ化処理
    return sql::SQLString(strValue);    //SQLString型に変更
}

これは実際に動かして確認した方が分かりやすいです。

今回とは別に、プロジェクトを用意しました。
簡易的な処理を書きます。

#include <string>

BOOL CMFCApplication1Dlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO: 初期化をここに追加します。
	CString strMes = _T("12345 → ") + strHashing(_T("12345"));
	strMes += _T("\r\nabcdefghijklmn → ") + strHashing(_T("abcdefghijklmn"));
	strMes += _T("\r\nThis is a pen → ") + strHashing(_T("This is a pen"));
	CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC);
	pStatic->SetWindowText(strMes);

	return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}

CString CMFCApplication1Dlg::strHashing(CString strPassword)
{
	CT2CA pszConvertedAnsiString(strPassword);
	std::string strStd(pszConvertedAnsiString);
	std::hash<std::string> hash_fn;

	std::string strValue = std::to_string(hash_fn(strStd)); //ハッシュ化処理
	CString strTemp = (CString)strValue.c_str();	//CString化

	return strTemp;
}

「12345」「abcdefghijklmn」「This is a pen」をそれぞれハッシュ化してみます。

こんな感じに20桁(多分)の数字の羅列になります。

このハッシュ化された数字(元パスワード)を、テーブルに格納します。

ユーザ情報変更処理

いよいよ今回の最後、ユーザ情報変更処理です。

/**
 * @fn
 * @brief	ユーザテーブル変更処理
 * @param	SQLMode enMode      enInsert    作成
 *                              enUpdate    変更
 *                              enDelete    削除
 *          CString strUserID               ユーザID
 *          CStinrg strUserName             ユーザ名
 *          CString strUserPassword         ユーザパスワード
 *          CString strUserAuth             ユーザ権限
 * @return	TRUE    操作成功
 *          FALSE   操作失敗
 */
BOOL CMySQLManager::bUserTableChange(SQLMode enMode, CString strUserID, CString strUserName, CString strUserPassword, CString strUserAuth)
{
    try
    {
        m_con->setSchema(SCHEMA);   //スキーマ選択
        sql::SQLString strslqUserID = strConverter(strUserID);    //ID型変換
        sql::SQLString strSqlQuery = "SELECT * FROM " + TABLE_USER + " WHERE Id = " + strslqUserID;

        // クエリを実行
        std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));
        std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());

        if (res->next())
        {
            //IDがユーザテーブルに存在した
            if (enMode == enInsert)
            {
                // 作成時はエラー
                AfxMessageBox(_T("既にIDが登録されています。"), MB_OK | MB_ICONQUESTION);
                return FALSE;
            }
        }
        else
        {
            //IDがユーザテーブルに存在しない
            if (enMode == enUpdate)
            {
                // 変更時はエラー
                AfxMessageBox(_T("IDが存在しないため更新処理が行えません。"), MB_OK | MB_ICONQUESTION);
                return FALSE;
            }
            else if (enMode == enDelete)
            {
                // 削除時はエラー
                AfxMessageBox(_T("IDが存在しないため削除処理が行えません。"), MB_OK | MB_ICONQUESTION);
                return FALSE;
            }
        }

        sql::SQLString strsqlUserName = strConverter(strUserName);
        sql::SQLString strsqlUserPass = strHashing(strUserPassword);
        sql::SQLString strsqlUserAuth = strConverter(strUserAuth);

        if (enMode == enInsert)
        {
            // 作成
            strSqlQuery = "INSERT INTO " + TABLE_USER + " VALUES (" + strslqUserID + ", '" + strsqlUserName + "', '" + strsqlUserPass + "', " + strsqlUserAuth + ")";
        }
        else if (enMode == enUpdate) 
        {
            // 変更
            strSqlQuery = "UPDATE " + TABLE_USER + " SET Name = '" + strsqlUserName + "', Password = '" + strsqlUserPass + "', Authority = " + strsqlUserAuth + " WHERE Id = " + strslqUserID + "";
        }
        else if (enMode == enDelete)
        {
            strSqlQuery = "DELETE FROM " + TABLE_USER + " WHERE Id = " + strslqUserID;
        }

        //実行
        std::unique_ptr<sql::PreparedStatement> pstmt2(m_con->prepareStatement(strSqlQuery));
        pstmt2->executeUpdate();

    }
    catch (sql::SQLException& e)
    {
        // 実行できなかった
        ErrSQLException(e);
        return FALSE;
    }

    return TRUE;
}
  • 20行目:引数のIDが存在するか確認するSQLです。
  • 26~35行目:IDが存在するが、それがINSERTの場合は作成拒否します。
  • 36~51行目:IDが存在しないが、それがUPDATEかDELETEの場合は操作拒否します。
  • 57~70行目:モードに合わせてSQL文を作ります。
  • 73~74行目:実行します。

さて、これで一度動かしてみます。

動作確認

現在のユーザテーブルはこんな感じ。

まずは存在するID「00000」で作成を試みようとすると

テーブル操作はできませんでした。
存在しないIDでUPDATE、DELETEをしようとしても同じく操作できません。

続いて、新規作成します。

完了しているみたいですね。

テーブルを見ますと

行が追加されてました。
パスワードもハッシュ化されてますね。

変更を行うと

変更が反映されてます。

最後に削除。

削除も実行されました。

これでユーザ関連はほぼおしまいです。

既存のID「00000」と「00001」は、変更から全く同じ情報を入れ、
パスワードをハッシュ化しておきます。

微修正

ログイン処理

このままだと、ID「00000」でログインしようとすると、
入力された生のパスワードとテーブルのハッシュ済パスワードを比較してしまうため、
ログインの方も処理を変更します。

MySQLManager.cppで、以前作成したログイン処理を少し変更。

/**
 * @fn
 * @brief	ログインチェック
 * @param	CString strId       ログインID
 *          CString strPassword ログインパスワード
 * @return	TRUE    ログイン成功
 *          FALSE   ログイン失敗
 */
BOOL CMySQLManager::bCheckLogin(CString strId, CString strPassword)
{
    try
    {
        m_con->setSchema(SCHEMA);   // スキーマ選択

        sql::SQLString sqlStrId = strConverter(strId);        // 型変換
        //sql::SQLString sqlStrPass = strConverter(strPassword);  // 型変換
        sql::SQLString sqlStrPass = strHashing(strPassword);    //型変換 ハッシュ化

        sql::SQLString strSqlQuery = "SELECT * FROM " + TABLE_USER +  " WHERE Id = '" + sqlStrId + "' AND Password = '" + sqlStrPass + "'";

        // クエリを実行
        std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));

        std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
        //略

16行目はもともとある処理ですが、
それをやめて17行目に変更しました。
後は同じです。

これで、ハッシュ化済パスワード同士を比較するので、
今まで通りのパスワードで入力でき、テーブルには生パスは残りません。

ユーザリスト

今の処理だと、
ユーザ情報変更が成功するたび、リストにどんどん情報が上書きされます。

表示がおかしいのは置いといてください(;´・ω・)
既にあるリストに再度ユーザテーブルから全行を取得して追加しているため、
同じ情報が何度も追加されます。

これは完全に凡ミスでした。
UserManage.cppから該当処理を変更します。
リストを初期化すればいいだけですね。

void CUserManage::ListUpdate()
{
	m_listUser.DeleteAllItems();	//リスト初期化
	CMySQLManager UserManage;
	UserManage.GetConnection();	//接続確立

	if (UserManage.bGetUserList(GetList()) == TRUE)	// ユーザテーブルからリストをゲット。
	{
		UpdateData(FALSE);
	}
	else
	{
		AfxMessageBox(_T("ユーザリスト取得時にエラーが生じました。"));
	}
}

3行目を追加しただけです。
これで何度も同じ行が表示されることはありません。

後、メッセージボックスのアイコンも変えようかなとは思います。
なんか、統一されていませんからね。
これは気が向いたら裏でこそこそ直しておきます。
ここで書くレベルではないので。

終わりに

ユーザテーブルの情報を変更する処理を書きました。

…正直、やり切った感はある。
まだ項目情報や申請もやらなきゃいけないのに( ;∀;)

次回は項目関連。
これとほぼ同じことをするので、そんなに作成に時間はかからないかなぁ…?

今回はここまで。

MFCで物品購入承認システム作成 7
前回までの話。MFCで物品購入承認システム作成 1MFCで物品購入承認システム作成 2MFCで物品購入承認システム作成 3MFCで物品購入承認システム作成 4MFCで物品購入承認システム作成 5MFCで物品購入承認システム作成 6グループテ...

コメント

タイトルとURLをコピーしました