Search A.I.
Menu
ホーム

メールマガジン ホームページプログラミングテク

Windowsテク
Javaアプレット サンプル
Java Q & A
JavaScript Q & A
Perl Q & A
Perl レッスン
PHP レッスン
PHPテク
MS-DOS コマンド集
UNIX コマンド集
SQL コマンド集
SEの基礎講座
WEBシステム開発受注します
]無料フォームメール送信サービス
リンク集
Perl レッスン

1. Perl の基礎
2. Perlの文法、条件式
3. Perlの文法、条件式 - 続き
4. 配列について
5. ハッシュ配列について
6. Perlの基礎 - 続き
7. 関数について
8. 関数について - 続き
9. 文字列操作関数
10. 配列操作関数
11. ファイル操作
12. ファイル操作 - 続き
13. パラメータの受け渡し
14. クッキーについて
15. 総合練習
16. メール送信
17. ファイルアップロード
18. パッケージの使用方法
19. MySQL
20. MySQL - 続き(1)
21. MySQL - 続き(2)
22. PostgreSQL
23. 総合練習
24. 総合練習解答



24. 総合練習解答

今日で全てのレッスンが終了になります。前回の解答を確認してください。

★前回の課題の解答

1. 以下の条件を満たすプログラムを作成してください。

(1) ログイン機能、ユーザ管理機能を作成します。
(2) データベースは、MySQLまたはPostgreSQLを使用し、テーブルは、ユーザ
テーブルを作成してください。ユーザテーブルの項目は、ユーザ名、
パスワード、メールアドレスとします。データの方や文字数については、
設計してください。
(3) ユーザ管理機能は、ユーザ一覧表示、ユーザ登録、ユーザ変更、ユーザ削除
を行います。
(4) ユーザのデータ項目は、ユーザ名、パスワード、メールアドレスとします。
(5) ログイン画面には、ユーザ名、パスワードのテキストボックスと、ログイン
ボタンを表示します。
(6) ログイン画面からログインボタンをクリックすると、ユーザテーブルのユーザ
名とパスワードが一致しているかを検索し、一致しているデータがなければ
ログインエラーとします。一致していれば、ユーザ一覧表示画面を表示します。
(7) ユーザ一覧画面は、登録されているユーザの、ユーザ名、メールアドレスを
全て表示します。また、ユーザ名をリンク表示し、リンクをクリックすると、
ユーザ変更画面を表示します。また、ユーザ一覧画面に、ユーザ新規登録
ボタンを表示し、クリックしたら、ユーザ登録画面を表示します。
(8) ユーザ登録画面は、ユーザ名、パスワード、メールアドレスのテキスト
ボックスと、登録ボタンを表示し、登録ボタンをクリックすると、ユーザ
データを新規登録します。ユーザ名は、半角英数字のみで必須、パスワードは
半角英数字のみで必須、メールアドレスは、メールアドレスチェックを行って
ください。
(9) ユーザ変更画面は、ユーザ名、パスワード、メールアドレスのテキスト
ボックスと、変更ボタン、削除ボタンを表示する。各テキストボックスには、
前のユーザ一覧表示画面で選択されたユーザデータをデフォルト表示します。
変更ボタンをクリックすると、該当するユーザデータの内容を変更します。
登録と同様にエラーチェックを行ってください。削除ボタンをクリックすると、
該当するユーザデータの内容を削除します。
(10) 画面のデザインについては、指定はありません。
(11) ログイン画面以外は、ログインしているユーザ以外は見れないようにして
ください。(クッキーを使用)

→下のサンプルを参照してください。

index.cgi
-----------------------------------------------------------------
#! /usr/local/bin/perl

print "Content-type: text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
<center>

<form action="login.cgi" method="post">
ログイン名 <input type="text" name="login_name" size="20"><br>
パスワード <input type="password" name="password" size="20"><br>
<br>
<input type="submit" value="ログイン">
</form>

</center>
</body>
</html>
END_OF_HTML

exit;
-----------------------------------------------------------------

common.pl
-----------------------------------------------------------------
package Common;
use DBI;

#------------------------------------------------------------------
# 関数 : ConnectDB
# 概要 : データベース接続処理
# 引数 : なし
# 戻値 : データベースコネクション
#------------------------------------------------------------------
sub ConnectDB
{
my $dbn = "test";
my $user = "admin";
my $pswd = "admin";

# データベース接続
my $conn = DBI->connect('DBI:mysql:' . $dbn . ':localhost', $user, $pswd);
if(!$conn)
{
DspMsg("データベースの接続に失敗しました");
exit;
}
return $conn;
}

#------------------------------------------------------------------
# 関数 : CloseDB
# 概要 : データベース切断
# 引数 : データベースコネクション
# 戻値 : なし
#------------------------------------------------------------------
sub CloseDB()
{
my($conn) = @_[0];
$conn->disconnect;
}

#------------------------------------------------------------------
# 関数 : CheckCookie
# 説明 : クッキーによるログイン認証
# 引数 : データベースコネクション
# 戻値 : 1:認証成功、それ以外:認証失敗(エラー画面を表示して終了)
#------------------------------------------------------------------
sub CheckCookie
{
my($conn) = @_[0];

# クッキー取得
my %cook = GetCookie();
return CheckLogin($conn, $cook{"login_name"}, $cook{"password"});
}

#------------------------------------------------------------------
# 関数 : CheckLogin
# 説明 : ログイン認証
# 引数 : データベースコネクション、ログイン名、パスワード
# 戻値 : 1:認証成功、それ以外:認証失敗(エラー画面を表示して終了)
#------------------------------------------------------------------
sub CheckLogin
{
my($conn) = @_[0];
my $login_name = @_[1];
my $password = @_[2];

# データベース読み込み
my $sql = "
select
count(*) as cnt
from
login_user
where
login_name = " . EscapeSQL($login_name) . "
and
password = " . EscapeSQL($password) . "
";
my $select = $conn->prepare($sql);
my $rec = $select->execute;
if(!$rec)
{
DspMsg("データベースエラー : " . $sql);
exit;
}

my @data = $select->fetchrow;
if ($data[0] > 0) {
return 1;
}

DspMsg("ログインに失敗しました。<br>\n<br>\n<br>\n<a href=\"index.cgi\">ログイン画面へ</a>");

exit;
}

#------------------------------------------------------------------
# 関数 : DspMsg
# 概要 : メッセージ表示
# 引数 : メッセージ文字列
# 戻値 : なし
#------------------------------------------------------------------
sub DspMsg
{
my($msg) = @_[0];

print << "END_OF_HTML";
Content-type: text/html;

<HTML>
<HEAD>
<TITLE>$title</TITLE>
</HEAD>
<BODY>
<P><FONT style="font-size=9pt">$msg</FONT></P>
</BODY>
</HTML>
END_OF_HTML

return;
}

#------------------------------------------------------------------
# 関数 : GetPara
# 概要 : パラメータ取得
# 引数 : なし
# 戻値 : パラメータハッシュ配列
#------------------------------------------------------------------
sub GetPara
{
my $self = shift;
my($query_string); #// エンコードされたパラメータ全体
my(@a, $a); #// エンコードされたパラメータを分解したもの
my($name, $value); #// デコードされたパラメータ
my(%in);

#// パラメータの読み込み
if ($ENV{"REQUEST_METHOD"} eq "POST")
{
#// POSTなら標準入力から読み込む
read(STDIN, $query_string, $ENV{"CONTENT_LENGTH"});
}
else
{
#// GETなら環境変数から読み込む
$query_string = $ENV{"QUERY_STRING"};
}

#// 「変数名1=値1&変数名2=値2」の形式をアンパサンド(&)で分解
@a = split(/&/, $query_string);

# パラメータの取得
foreach $a(@a)
{
#// =(イコール)で分解
($name, $value) = split(/=/, $a);
#// + や %8A などのデコード
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex( $1 ))/eg;
#// 後で使用するため,$in{'パラメータ名'} に代入しておく
$in{$name} = $value;
}

return %in;
}

#------------------------------------------------------------------
# 関数 : SetCookie
# 説明 : クッキー設定
# 引数 : 保存する文字列
# : クッキーの有効時間(秒)
# 戻値 : なし
#------------------------------------------------------------------
sub SetCookie
{
my($data, %data, $str_cookie, $cookie_time);
my($sec, $min, $hour, $mday, $mon, $year, $wday);
my(@mons, @week, $dt);

$str_cookie = @_[0];
$cookie_time = @_[1];

#// 文字列のエスケープ
$str_cookie =~ s/\,/%2C/g;
$str_cookie =~ s/;//g;
$str_cookie =~ s/([^\w\=\& ])/'%' . unpack("H2", $1)/eg;

#// 有効期限設定
($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time + $cookie_time);
@mons = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
@week = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
$dt = sprintf("%s\, %02d-%s-%04d %02d:%02d:%02d GMT", $week[$wday], $mday, $mons[$mon], $year+1900, $hour, $min, $sec);

#// ヘッダ出力
print "Set-Cookie: $str_cookie expires=$dt;\n";
}

#------------------------------------------------------------------
# 関数 : GetCookie
# 概要 : クッキー取得
# 引数 : なし
# 戻値 : クッキーの値
#------------------------------------------------------------------
sub GetCookie
{
my($cookie) = $ENV{'HTTP_COOKIE'};
my($key, $val, %data);
my(@cookie);

#// クッキー取得
@cookie = split(/ /, $cookie);
foreach(@cookie)
{
($key, $val) = split(/=/);

#// 文字列をデコードする
$val =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
$val =~ s/%2C/\,/g;
$val =~ s/%20/ /g;
$data{$key} = $val;
}

#// 戻り値セット
return %data;
}

#------------------------------------------------------------------
# 関数 : EscapeSQL
# 概要 : SQLエスケープ処理
# 引数 : 変換する文字列
# 戻値 : エスケープ処理後の文字列
#------------------------------------------------------------------
sub EscapeSQL
{
my $str = @_[0];
if ($str eq '')
{
return "null";
}

$str =~ s/'/''/g;
return "'" . $str . "'";
}

1;
-----------------------------------------------------------------

login.cgi
-----------------------------------------------------------------
#! /usr/local/bin/perl

require './common.pl';
use strict;
use DBI;

# パラメータ取得
my %data = Common::GetPara();
my $login_name = $data{"login_name"};
my $password = $data{"password"};

# データベース接続
my $conn = Common::ConnectDB();

# ログイン認証処理
Common::CheckLogin($conn, $login_name, $password);

# データベースクローズ
Common::CloseDB($conn);

# データエスケープ
$login_name =~ s/ /%20/g;
$password =~ s/ /%20/g;

# クッキーセット処理
my $strCookie = "login_name=" . $login_name . " password=" . $password;
Common::SetCookie($strCookie, 3*60*60);

print "Content-type: text/html\n\n";

# ユーザ一覧画面へ遷移させる
print << "END_OF_HTML";
<html>
<head>
<meta http-equiv="refresh" content="0;url=user_list.cgi">
</head>
</html>
END_OF_HTML

exit;
-----------------------------------------------------------------

user_list.cgi
-----------------------------------------------------------------
#! /usr/local/bin/perl
# ユーザ一覧表示画面

require './common.pl';
use strict;
use DBI;

# データベース接続
my $conn = Common::ConnectDB();

# ログイン認証処理
Common::CheckCookie($conn);

# ユーザ一覧取得
my $sql = "
select
login_id,
login_name,
email
from
login_user
";

# データベース読み込み
my $select = $conn->prepare($sql);
my $rec = $select->execute;
if(!$rec)
{
Common::DspMsg("データベースエラー : " . $sql);
exit;
}

# データベースクローズ
Common::CloseDB($conn);

print "Content-type: text/html\n\n";

print << "END_OF_HTML";
<html>
<body>
<center>
ユーザ一覧<br>
<br>
<table border="1">
<tr>
<td>ユーザ名</td>
<td>メールアドレス</td>
</tr>
END_OF_HTML

my (@user);
while (@user = $select->fetchrow) {
print "<tr><td><a href=\"user_up.cgi?uid=" . $user[0] . "\">" . $user[1] . "</a></td>";
print "<td>" . $user[2] . "</td></tr>\n";
}

print << "END_OF_HTML";
</table>
<br>
<form action="user_up.cgi" method="get">
<input type="submit" value="ユーザ登録">
</form>
</center>
</body>
</html>
END_OF_HTML

exit;
-----------------------------------------------------------------

user_up.cgi
-----------------------------------------------------------------
#! /usr/local/bin/perl
# ユーザ一覧表示画面

require './common.pl';
use strict;
use DBI;

my($login_name, $password, $email);
my $mode = "登録";

# パラメータ取得
my %data = Common::GetPara();

# データベース接続
my $conn = Common::ConnectDB();

# ログイン認証処理
Common::CheckCookie($conn);

if ($data{"uid"}) {
# 変更の場合
$mode = "変更";

# ユーザ情報取得
my $sql = "
select
login_id,
login_name,
password,
email
from
login_user
where
login_id = " . Common::EscapeSQL($data{"uid"}) . "
";

# データベース読み込み
my $select = $conn->prepare($sql);
my $rec = $select->execute;
if(!$rec)
{
Common::DspMsg("データベースエラー : " . $sql);
exit;
}

my @user;
@user = $select->fetchrow;
$login_name = $user[1];
$password = $user[2];
$email = $user[3];
}

# データベースクローズ
Common::CloseDB($conn);

print "Content-type: text/html\n\n";

print << "END_OF_HTML";
<html>
<body>
<center>
ユーザ$mode<br>
<br>
<table border="1">
<form action="post.cgi" method="post">
<input type="hidden" name="login_id" value="$data{"uid"}">
<tr>
<td>ユーザ名</td>
<td><input type="text" name="login_name" value="$login_name" size="20"></td>
</tr>
<tr>
<td>パスワード</td>
<td><input type="text" name="password" value="$password" size="20"></td>
</tr>
<tr>
<td>メールアドレス</td>
<td><input type="text" name="email" value="$email" size="30"></td>
</tr>
</table>
<br>
<input type="submit" value="ユーザ$mode">
END_OF_HTML

if ($data{"uid"}) {
print "<input type=\"submit\" name=\"del\" value=\"ユーザ削除\">\n";
}

print << "END_OF_HTML";
<input type="button" value="戻る" onClick="javascript:history.back();">
</form>
</center>
</body>
</html>
END_OF_HTML

exit;
-----------------------------------------------------------------

post.cgi
-----------------------------------------------------------------
#! /usr/local/bin/perl

require './common.pl';
use strict;
use DBI;

# パラメータ取得
my %data = Common::GetPara();
my $login_id = $data{"login_id"};
my $login_name = $data{"login_name"};
my $password = $data{"password"};
my $email = $data{"email"};

# データベース接続
my $conn = Common::ConnectDB();

# トランザクション開始
my $select = $conn->prepare("begin");
my $rec = $select->execute;
if(!$rec)
{
Common::DspMsg("トランザクション開始処理に失敗しました");
exit;
}

# ログイン認証処理
Common::CheckCookie($conn);

# エラーチェック
if (!$login_name) {
Common::DspMsg("ユーザ名を入力してください。");
# データベースクローズ
Common::CloseDB($conn);
exit;
}
elsif ($login_name !~ /^[0-9a-zA-Z]/) {
Common::DspMsg("ユーザ名は半角英数字で入力してください。");
# データベースクローズ
Common::CloseDB($conn);
exit;
}

if (!$password) {
Common::DspMsg("パスワードを入力してください。");
# データベースクローズ
Common::CloseDB($conn);
exit;
}
elsif ($password !~ /^[0-9a-zA-Z]/) {
Common::DspMsg("パスワードは半角英数字で入力してください。");
# データベースクローズ
Common::CloseDB($conn);
exit;
}

if ($email !~ /^.*@.*\./) {
Common::DspMsg("メールアドレスを正しく入力してください。");
# データベースクローズ
Common::CloseDB($conn);
exit;
}

my $sql;
if ($data{"del"})
{
# ユーザ削除処理の場合
$sql = "
delete from
login_user
where
login_id = " . Common::EscapeSQL($login_id) . "
";
}
elsif ($data{"login_id"})
{
# ユーザ更新処理の場合
$sql = "
update
login_user
set
login_name = " . Common::EscapeSQL($login_name) . ",
password = " . Common::EscapeSQL($password) . ",
email = " . Common::EscapeSQL($email) . "
where
login_id = " . Common::EscapeSQL($login_id) . "
";
}
else {
# ユーザ新規登録の場合
$sql = "
insert into login_user (
login_name,
password,
email
) values (
" . Common::EscapeSQL($login_name) . ",
" . Common::EscapeSQL($password) . ",
" . Common::EscapeSQL($email) . "
)";
}

# データベース書き込み
$select = $conn->prepare($sql);
$rec = $select->execute;
if(!$rec)
{
Common::DspMsg("データベースエラー : " . $sql);
exit;
}

# トランザクション終了
$select = $conn->prepare("commit");
$rec = $select->execute;
if(!$rec)
{
Common::DspMsg("コミット処理に失敗しました");
exit;
}

# データベースクローズ
Common::CloseDB($conn);

print "Content-type: text/html\n\n";

# ユーザ一覧画面へ遷移させる
print << "END_OF_HTML";
<html>
<head>
<meta http-equiv="refresh" content="0;url=user_list.cgi">
</head>
</html>
END_OF_HTML

exit;
-----------------------------------------------------------------

■解説
かなり長いですが、全ファイルのソースを記述いたしました。
ファイル構成は以下の通りです。

index.cgi : ログイン画面表示
common.pl : 共通処理ファイル
login.cgi : ログイン処理
user_list.cgi : ユーザ一覧画面表示
user_up.cgi : ユーザ登録・変更画面表示
post.cgi : ユーザ登録・変更処理

ログイン画面表示は.htmlでも構いません。
index.cgiから、ログインボタンを押すと、login.cgiに飛び、ログイン認証を行い
ます。失敗すると、エラー画面を表示して終了します。なお、上記サンプルでは、
ログイン認証処理は、一括してcommon.pl内で行っています。

ログインに成功すると、クッキーにログイン情報を書き込んで、ユーザ一覧画面を
表示します。

ユーザ登録画面と、ユーザ変更画面は、同じファイルにしています。ユーザ変更の
場合は、uidというパラメータを渡すようにし、uidがある場合は、変更処理に
なるようにします。

ユーザ登録・変更画面から、更新処理をする場合は、どの処理でもpost.cgiに飛ぶ
ようにし、データベースに書き込んでいます。エラーがなければ、更新処理後、
ユーザ一覧画面を表示します。

詳細の処理については、上記サンプルを確認してください。

クッキーは、3時間有効にしていますので、ログイン後、3時間が経過すると、自動
的にログアウトしてしまいます。画面遷移するごとに、クッキーの有効時間を更新
した方が親切ですが、ここでは省略します。




前の章へ


このエントリーをはてなブックマークに追加


OfficeLance

お問い合わせはこちらから