
サーブレット&JSPの基本|みんなのショッピングのログイン機能を作成しよう
設計したログイン機能を、順番にプログラムへ落とし込む。Entity、DAO、BO、サーブレット、JSPをつなげて、みんなのショッピングのログイン処理を完成させよう
前の記事では、ショッピングサイト「みんなのショッピング」のログイン機能を設計しました。
ログイン機能を作るために、どのデータベースを使うのか、どのテーブルに会員情報を保存するのか、どの画面を用意するのか、どのサーブレットとJSPを使うのか、そしてサーバサイドでどのクラスを連携させるのかを整理しました。
今回の記事では、その設計をもとに、実際にプログラムを作成します。
プロジェクト名はstudyです。
データベース名はminnaMarketです。
テーブル名はCUSTOMERSです。
テスト用アカウントは、ユーザーIDをmiyu、パスワードを1234とします。
ログイン機能では、画面から入力されたユーザーIDとパスワードを受け取り、CUSTOMERSテーブルに登録されている会員情報と照合します。
一致する会員が見つかればログイン成功です。
ログイン成功時は、ユーザーIDをセッションスコープに保存し、ログイン成功画面を表示します。
一致する会員が見つからなければログイン失敗です。
ログイン失敗時は、ログイン画面へ戻します。
いきなりサーブレットやJSPから作り始めると、エラーが起きたときに原因を探しにくくなります。
そのため、この記事では次の順番で作成します。
作成の流れ
| 順番 | 作業 | 内容 |
|---|---|---|
| 1 | Entityクラスを作成する | ログイン情報と会員情報を表すクラスを作る |
| 2 | DAOを作成する | CUSTOMERSテーブルを検索するクラスを作る |
| 3 | DAOをテストする | データベース検索が正しく動くか確認する |
| 4 | BOを作成する | ログイン判定を行うクラスを作る |
| 5 | BOをテストする | ログイン成功と失敗を確認する |
| 6 | 画面遷移を作成する | サーブレットとJSPで画面の流れを先に作る |
| 7 | 画面遷移をテストする | ブラウザで画面の移動を確認する |
| 8 | サーブレットを仕上げる | リクエストパラメータ取得、BO呼び出し、スコープ処理を追加する |
| 9 | JSPを仕上げる | セッションスコープの値をEL式とJSTLで表示する |
| 10 | 機能を動作確認する | ログイン成功とログイン失敗をブラウザで確認する |
この順番で作ると、どこまで正しく動いているかを段階的に確認できます。
特にDAOとBOは、一般的なJavaクラスとしてテストしやすいので、先に完成させておくと安心です。
まずモデルから作成する
ログイン機能を作るとき、最初にJSPを書きたくなるかもしれません。
画面が見えると作っている実感が出るためです。
しかし、ログイン機能ではデータベース検索とログイン判定が重要です。
この部分が正しく動いていないと、サーブレットやJSPを作っても、ログイン処理全体がうまく動きません。
そこで、まずはモデル側のクラスから作成します。
ここでいうモデル側のクラスとは、Entity、DAO、BOです。
モデル側で作成するクラス
| 種類 | クラス名 | パッケージ | 役割 |
|---|---|---|---|
| Entity | CustomerLogin | model | 入力されたログイン情報を表す |
| Entity | CustomerAccount | model | CUSTOMERSテーブルの1件分の会員情報を表す |
| DAO | CustomersDAO | dao | CUSTOMERSテーブルを検索する |
| BO | CustomerLoginLogic | model | ログイン認証の成否を判定する |
Entityは、データを入れて運ぶためのクラスです。
DAOは、データベースアクセスを担当するクラスです。
BOは、アプリケーションの中心となる処理を担当するクラスです。
今回であれば、CustomerLoginLogicがログイン処理を担当します。
図1:モデルから作ってテストする流れ

この図から分かること
ログイン機能を作るときは、まずモデル側のクラスを作成します。
CustomerLoginは、ログイン画面から入力されたユーザーIDとパスワードを表します。
CustomerAccountは、CUSTOMERSテーブルの1件分の会員情報を表します。
CustomersDAOは、CUSTOMERSテーブルを検索します。
CustomerLoginLogicは、DAOの結果をもとにログイン成功か失敗かを判定します。
サーブレットやJSPを作る前にDAOとBOをテストしておくと、後からエラーが出たときに原因を絞り込みやすくなります。
ログイン情報を表すEntityを作成する
最初に、ログイン画面から入力された情報を表すEntityを作成します。
このクラスをCustomerLoginとします。
ログイン画面では、ユーザーIDとパスワードを入力します。
そのため、CustomerLoginクラスにはcustomerIdとloginPassの2つのフィールドを用意します。
CustomerLoginクラスのフィールド
| フィールド | 内容 |
|---|---|
| customerId | ログイン画面で入力されたユーザーID |
| loginPass | ログイン画面で入力されたパスワード |
CustomerLoginインスタンスは、CustomerLoginServletで作成されます。
その後、CustomerLoginLogicへ渡され、さらにCustomersDAOへ渡されます。
ログイン情報を表すEntityの例
ファイル名: CustomerLogin.java
package model;
public class CustomerLogin {
private String customerId;
private String loginPass;
public CustomerLogin(String customerId, String loginPass) {
this.customerId = customerId;
this.loginPass = loginPass;
}
public String getCustomerId() {
return customerId;
}
public String getLoginPass() {
return loginPass;
}
}CustomerLogin.javaは、modelパッケージに作成します。
このクラスは、ログイン画面で入力されたユーザーIDとパスワードをまとめて扱うためのクラスです。
コンストラクタでは、customerIdとloginPassを受け取り、それぞれのフィールドに保存します。
getterメソッドを用意しているため、CustomersDAOではlogin.getCustomerIdやlogin.getLoginPassを使って値を取り出せます。
CUSTOMERSテーブルのレコードを表すEntityを作成する
次に、CUSTOMERSテーブルの1件分の会員情報を表すEntityを作成します。
このクラスをCustomerAccountとします。
CUSTOMERSテーブルには、CUSTOMER_ID、LOGIN_PASS、EMAIL、CUSTOMER_NAME、AGEの列があります。
Java側では、これらに対応するフィールドを用意します。
CustomerAccountクラスのフィールド
| フィールド | 対応する列 | 内容 |
|---|---|---|
| customerId | CUSTOMER_ID | ユーザーID |
| loginPass | LOGIN_PASS | パスワード |
| メールアドレス | ||
| customerName | CUSTOMER_NAME | 氏名 |
| age | AGE | 年齢 |
CustomersDAOで会員情報が見つかった場合、ResultSetから取り出した値を使ってCustomerAccountインスタンスを作成します。
CUSTOMERSテーブルのレコードを表すEntityの例
ファイル名: CustomerAccount.java
package model;
public class CustomerAccount {
private String customerId;
private String loginPass;
private String email;
private String customerName;
private int age;
public CustomerAccount(String customerId, String loginPass,
String email, String customerName, int age) {
this.customerId = customerId;
this.loginPass = loginPass;
this.email = email;
this.customerName = customerName;
this.age = age;
}
public String getCustomerId() {
return customerId;
}
public String getLoginPass() {
return loginPass;
}
public String getEmail() {
return email;
}
public String getCustomerName() {
return customerName;
}
public int getAge() {
return age;
}
}CustomerAccount.javaは、modelパッケージに作成します。
このクラスは、CUSTOMERSテーブルの1件分の会員情報をJavaで扱うためのクラスです。
DAOがデータベースから会員情報を取得したとき、その結果をCustomerAccountインスタンスとして返します。
ログイン成功かどうかを判断するときは、CustomerAccountインスタンスが取得できたかどうかを確認します。
DAOを作成する
次に、CUSTOMERSテーブルを担当するDAOを作成します。
このクラスをCustomersDAOとします。
CustomersDAOには、findByLoginメソッドを用意します。
findByLoginメソッドは、CustomerLoginインスタンスを受け取り、CUSTOMERSテーブルから該当する会員を検索します。
findByLoginメソッドの役割
| 項目 | 内容 |
|---|---|
| 引数 | CustomerLogin |
| 処理 | CUSTOMER_IDとLOGIN_PASSが一致するレコードを検索する |
| 戻り値 | 見つかった場合はCustomerAccount、見つからない場合はnull |
| 使用するSQL | SELECT文 |
| 接続先 | minnaMarketデータベース |
ログイン処理では、入力されたユーザーIDとパスワードの両方が一致するかどうかを確認します。
そのため、WHERE句ではCUSTOMER_IDとLOGIN_PASSの両方を条件にします。
CUSTOMERSテーブルを担当するDAOの例
ファイル名: CustomersDAO.java
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import model.CustomerAccount;
import model.CustomerLogin;
public class CustomersDAO {
private final String JDBC_URL =
"jdbc:h2:tcp://localhost/~/minnaMarket";
private final String DB_USER = "sa";
private final String DB_PASS = "";
public CustomerAccount findByLogin(CustomerLogin login) {
CustomerAccount account = null;
// JDBCドライバを読み込む
try {
Class.forName("org.h2.Driver");
} catch (ClassNotFoundException e) {
throw new IllegalStateException(
"JDBCドライバを読み込めませんでした");
}
// データベースへ接続
try (Connection conn = DriverManager.getConnection(
JDBC_URL, DB_USER, DB_PASS)) {
// SELECT文を準備
String sql = "SELECT CUSTOMER_ID, LOGIN_PASS, EMAIL, "
+ "CUSTOMER_NAME, AGE "
+ "FROM CUSTOMERS "
+ "WHERE CUSTOMER_ID = ? AND LOGIN_PASS = ?";
PreparedStatement pStmt = conn.prepareStatement(sql);
pStmt.setString(1, login.getCustomerId());
pStmt.setString(2, login.getLoginPass());
// SELECT文を実行し、結果表を取得
ResultSet rs = pStmt.executeQuery();
if (rs.next()) {
// 会員が存在したらデータを取得し、
// CustomerAccountインスタンスを作成する
String customerId = rs.getString("CUSTOMER_ID");
String loginPass = rs.getString("LOGIN_PASS");
String email = rs.getString("EMAIL");
String customerName = rs.getString("CUSTOMER_NAME");
int age = rs.getInt("AGE");
account = new CustomerAccount(customerId, loginPass,
email, customerName, age);
}
} catch (SQLException e) {
e.printStackTrace();
return null;
}
return account;
}
}CustomersDAO.javaは、daoパッケージに作成します。
JDBC_URLには、minnaMarketデータベースへ接続するためのJDBC URLを指定します。
findByLoginメソッドでは、CustomerLoginインスタンスからcustomerIdとloginPassを取り出し、PreparedStatementに設定しています。
SQL文の?には、pStmt.setStringで値を設定します。
1番目の?にはユーザーID、2番目の?にはパスワードを入れます。
該当する会員が見つかった場合は、CustomerAccountインスタンスを作成して返します。
見つからなかった場合は、accountの値はnullのままなので、nullが返ります。
DAOをテストする
DAOを作成したら、すぐに次のクラスへ進まず、DAOの動作を確認します。
findByLoginメソッドは、引数の値によって結果が変わります。
正しいユーザーIDとパスワードを渡した場合は、会員情報が取得できるはずです。
間違ったパスワードを渡した場合は、会員情報が取得できず、nullが返るはずです。
DAOテストで確認すること
| テスト | 入力 | 期待する結果 |
|---|---|---|
| ログイン成功のテスト | customerIdがmiyu、loginPassが1234 | CustomerAccountが返る |
| ログイン失敗のテスト | customerIdがmiyu、loginPassが9999 | nullが返る |
この2つを確認することで、CustomersDAOの基本的な動作を確認できます。
CustomersDAOをテストするクラスの例
ファイル名: CustomersDAOCheck.java
package test;
import dao.CustomersDAO;
import model.CustomerAccount;
import model.CustomerLogin;
public class CustomersDAOCheck {
public static void main(String[] args) {
testFindByLoginOK();
testFindByLoginNG();
}
public static void testFindByLoginOK() {
CustomerLogin login = new CustomerLogin("miyu", "1234");
CustomersDAO dao = new CustomersDAO();
CustomerAccount result = dao.findByLogin(login);
if (result != null
&& result.getCustomerId().equals("miyu")
&& result.getLoginPass().equals("1234")
&& result.getEmail().equals("miyu.tanaka@example.jp")
&& result.getCustomerName().equals("田中 美優")
&& result.getAge() == 28) {
System.out.println("testFindByLoginOK:成功しました");
} else {
System.out.println("testFindByLoginOK:失敗しました");
}
}
public static void testFindByLoginNG() {
CustomerLogin login = new CustomerLogin("miyu", "9999");
CustomersDAO dao = new CustomersDAO();
CustomerAccount result = dao.findByLogin(login);
if (result == null) {
System.out.println("testFindByLoginNG:成功しました");
} else {
System.out.println("testFindByLoginNG:失敗しました");
}
}
}CustomersDAOCheck.javaは、testパッケージに作成します。
このクラスはmainメソッドを持っているため、Javaアプリケーションとして実行できます。
Eclipseでは、CustomersDAOCheck.javaを選択して右クリックし、実行、Javaアプリケーションの順に選択します。
実行結果が次のようになれば、DAOのテストは成功です。
testFindByLoginOK:成功しました
testFindByLoginNG:成功しましたテストに失敗した場合は、CUSTOMERSテーブルのデータ、CustomerLoginクラス、CustomerAccountクラス、CustomersDAOクラスを確認します。
特に、データベース名、テーブル名、列名、パスワードの値に誤りがないか確認しましょう。
単体テストとは
CustomersDAOCheckのように、特定のクラスやメソッドが正しく動くかを確認するテストを単体テストと呼びます。
今回のようにmainメソッドを使って簡単に確認する方法でも、学習段階では十分役に立ちます。
本格的な開発では、JUnitなどの単体テストフレームワークを使うことがあります。
単体テストの利点
| 利点 | 内容 |
|---|---|
| 早い段階でミスに気付ける | サーブレットと連携する前にDAOの問題を発見できる |
| 原因を絞り込みやすい | DAOだけを実行して確認できる |
| 修正しやすい | Web画面を使わずにJavaクラスとして確認できる |
| 安心して次へ進める | DAOが正しく動くことを確認してからBOを作れる |
Webアプリケーション全体を一度に作ってから確認すると、エラーの原因がどこにあるのか分かりにくくなります。
こまめにテストしながら進めることが大切です。
BOを作成する
次に、ログイン処理を担当するBOを作成します。
このクラスをCustomerLoginLogicとします。
CustomerLoginLogicには、executeメソッドを用意します。
executeメソッドは、CustomerLoginインスタンスを受け取り、CustomersDAOのfindByLoginメソッドを呼び出します。
会員情報が取得できた場合はtrueを返します。
会員情報が取得できなかった場合はfalseを返します。
CustomerLoginLogicの役割
| 項目 | 内容 |
|---|---|
| クラス名 | CustomerLoginLogic |
| パッケージ | model |
| メソッド | execute |
| 引数 | CustomerLogin |
| 戻り値 | boolean |
| 処理 | CustomersDAOを使ってログイン成功か失敗かを判定する |
BOは、アプリケーションの中心となる処理を担当します。
今回であれば、ログインできるかどうかを判定するのがBOの役割です。
ログイン処理を担当するBOの例
ファイル名: CustomerLoginLogic.java
package model;
import dao.CustomersDAO;
public class CustomerLoginLogic {
public boolean execute(CustomerLogin login) {
CustomersDAO dao = new CustomersDAO();
CustomerAccount account = dao.findByLogin(login);
return account != null;
}
}CustomerLoginLogic.javaは、modelパッケージに作成します。
executeメソッドでは、CustomersDAOを作成し、findByLoginメソッドを呼び出します。
findByLoginメソッドの戻り値がnullでなければ、該当する会員が見つかったということです。
その場合はtrueを返します。
戻り値がnullであれば、該当する会員が見つからなかったということなので、falseを返します。
BOをテストする
BOを作成したら、DAOと同じようにテストします。
CustomerLoginLogicのexecuteメソッドは、ログイン成功時にtrue、ログイン失敗時にfalseを返す必要があります。
BOテストで確認すること
| テスト | 入力 | 期待する結果 |
|---|---|---|
| ログイン成功のテスト | customerIdがmiyu、loginPassが1234 | true |
| ログイン失敗のテスト | customerIdがmiyu、loginPassが9999 | false |
このテストに成功すれば、ログイン判定の中心部分が正しく動いていると確認できます。
CustomerLoginLogicをテストするクラスの例
ファイル名: CustomerLoginLogicCheck.java
package test;
import model.CustomerLogin;
import model.CustomerLoginLogic;
public class CustomerLoginLogicCheck {
public static void main(String[] args) {
testExecuteOK();
testExecuteNG();
}
public static void testExecuteOK() {
CustomerLogin login = new CustomerLogin("miyu", "1234");
CustomerLoginLogic bo = new CustomerLoginLogic();
boolean result = bo.execute(login);
if (result) {
System.out.println("testExecuteOK:成功しました");
} else {
System.out.println("testExecuteOK:失敗しました");
}
}
public static void testExecuteNG() {
CustomerLogin login = new CustomerLogin("miyu", "9999");
CustomerLoginLogic bo = new CustomerLoginLogic();
boolean result = bo.execute(login);
if (!result) {
System.out.println("testExecuteNG:成功しました");
} else {
System.out.println("testExecuteNG:失敗しました");
}
}
}CustomerLoginLogicCheck.javaは、testパッケージに作成します。
このクラスもmainメソッドを持っているため、Javaアプリケーションとして実行できます。
実行結果が次のようになれば、BOのテストは成功です。
testExecuteOK:成功しました
testExecuteNG:成功しましたここまでで、ログイン処理のモデル部分が完成しました。
CustomerLogin、CustomerAccount、CustomersDAO、CustomerLoginLogicが正しく動作することを確認できています。
この後は、サーブレットとJSPを作成し、ブラウザからログイン機能を使えるようにします。
図2:サーブレットとJSPで画面遷移を先に作る

この図から分かること
モデル部分を作成してテストしたら、次はサーブレットとJSPを作成します。
ただし、最初からログイン判定まで組み込むのではなく、まず画面遷移だけを作ります。
MarketTopServletからmarketTop.jspへフォワードし、ログインリンクからCustomerLoginServletへ移動します。
CustomerLoginServletのdoGetではcustomerLogin.jspを表示します。
最初の段階では、doPostはログイン成功画面へフォワードするだけにします。
画面遷移を先に確認しておくと、この後の仕上げ作業が進めやすくなります。
トップ画面を表示するサーブレットを作成する
ここからは、サーブレットとJSPを作成します。
まず、トップ画面を表示するMarketTopServletを作ります。
この段階では、特別な処理は行いません。
marketTop.jspへフォワードするだけです。
トップ画面に関するリクエストを処理するコントローラの例
ファイル名: MarketTopServlet.java
package servlet;
import java.io.IOException;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/MarketTopServlet")
public class MarketTopServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(
"WEB-INF/jsp/marketTop.jsp");
dispatcher.forward(request, response);
}
}MarketTopServlet.javaは、servletパッケージに作成します。
@WebServlet("/MarketTopServlet")により、ブラウザからMarketTopServletへアクセスできるようになります。
doGetでは、RequestDispatcherを使ってWEB-INF/jsp/marketTop.jspへフォワードしています。
JSPをWEB-INFの中に置くことで、JSPへ直接アクセスするのではなく、サーブレットを通して表示する流れにできます。
トップ画面を作成する
次に、トップ画面を表示するmarketTop.jspを作成します。
この画面には、ログイン画面へ移動するリンクと、ユーザー登録の項目を表示します。
ユーザー登録機能はまだ作成しないため、ここでは文字だけを表示します。
トップ画面を出力するビューの例
ファイル名: marketTop.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>みんなのショッピング</title>
</head>
<body>
<h1>みんなのショッピング</h1>
<ul>
<li><a href="CustomerLoginServlet">ログイン</a></li>
<li>ユーザー登録</li>
</ul>
</body>
</html>marketTop.jspは、src/main/webapp/WEB-INF/jspに配置します。
ログインリンクのhrefにはCustomerLoginServletを指定しています。
これにより、ログインをクリックするとCustomerLoginServletのdoGetが実行されます。
ログイン画面とログイン成功画面への遷移を作る
次に、ログイン画面を表示するCustomerLoginServletを作成します。
最初の段階では、ログイン処理はまだ組み込みません。
doGetではcustomerLogin.jspへフォワードします。
doPostでは、ログイン成功画面であるcustomerLoginOK.jspへフォワードします。
このように、まず画面遷移だけを完成させます。
ログインに関するリクエストを処理するコントローラの例
ファイル名: CustomerLoginServlet.java
package servlet;
import java.io.IOException;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/CustomerLoginServlet")
public class CustomerLoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(
"WEB-INF/jsp/customerLogin.jsp");
dispatcher.forward(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(
"WEB-INF/jsp/customerLoginOK.jsp");
dispatcher.forward(request, response);
}
}この段階のCustomerLoginServletでは、ログイン判定をしていません。
doPostが実行されると、常にcustomerLoginOK.jspへフォワードします。
これは、画面遷移を先に完成させるための仮の実装です。
後で、リクエストパラメータを取得し、CustomerLoginLogicを呼び出す処理を追加します。
ログイン画面を作成する
次に、ログイン画面を作成します。
ログイン画面では、ユーザーIDとパスワードを入力します。
入力された値は、POSTメソッドでCustomerLoginServletへ送信します。
ログイン画面を出力するビューの例
ファイル名: customerLogin.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>みんなのショッピング</title>
</head>
<body>
<h1>ログイン</h1>
<form action="CustomerLoginServlet" method="post">
ユーザーID:<input type="text" name="customerId"><br>
パスワード:<input type="password" name="loginPass"><br>
<input type="submit" value="ログイン">
</form>
</body>
</html>customerLogin.jspは、src/main/webapp/WEB-INF/jspに配置します。
formタグのactionにはCustomerLoginServletを指定します。
methodにはpostを指定します。
ユーザーIDのinputタグにはname属性としてcustomerIdを指定します。
パスワードのinputタグにはname属性としてloginPassを指定します。
このname属性の値は、後でCustomerLoginServletのrequest.getParameterで取得する名前と一致させます。
ログイン成功画面を仮作成する
次に、ログイン成功画面を作成します。
この段階では、まだセッションスコープにユーザーIDを保存していません。
そのため、ユーザーIDの部分はダミー表示にしておきます。
ログイン成功画面を出力するビューの例
ファイル名: customerLoginOK.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>みんなのショッピング</title>
</head>
<body>
<h1>ログイン成功</h1>
<p>ようこそ[ユーザーID]さん</p>
<a href="MarketTopServlet">トップへ</a>
</body>
</html>customerLoginOK.jspは、src/main/webapp/WEB-INF/jspに配置します。
この段階では、[ユーザーID]をダミーとして表示しています。
後でサーブレットを仕上げたあと、セッションスコープに保存されたcustomerIdをJSTLのc:outで表示するように変更します。
画面遷移をテストする
ここまでで、トップ画面、ログイン画面、ログイン成功画面の画面遷移を確認できます。
ブラウザから次のURLへアクセスします。
ブラウザの表示例

画面遷移の確認手順
| 手順 | 操作 | 確認すること |
|---|---|---|
| 1 | MarketTopServletへアクセスする | トップ画面が表示される |
| 2 | ログインをクリックする | ログイン画面が表示される |
| 3 | 任意のユーザーIDとパスワードを入力する | 入力欄に値を入力できる |
| 4 | ログインボタンをクリックする | ログイン成功画面が表示される |
| 5 | トップへをクリックする | トップ画面へ戻る |
この段階では、正しいユーザーIDやパスワードを入力しなくてもログイン成功画面へ進みます。

まだログイン判定を組み込んでいないためです。
ここで確認したいのは、あくまで画面遷移が正しく行われるかどうかです。
図3:仕上げ後のログイン処理の全体像

この図から分かること
仕上げ後のログイン処理では、customerLogin.jspからCustomerLoginServletへPOSTリクエストが送信されます。
CustomerLoginServletは、customerIdとloginPassを取得し、CustomerLoginインスタンスを作成します。
CustomerLoginLogicは、CustomersDAOを使ってCUSTOMERSテーブルを検索します。
ログインに成功した場合は、customerIdをセッションスコープに保存し、customerLoginOK.jspへフォワードします。
ログインに失敗した場合は、CustomerLoginServletへリダイレクトし、ログイン画面を再表示します。
サーブレットを仕上げる
画面遷移が確認できたら、CustomerLoginServletを仕上げます。
ここでは、doPostに次の処理を追加します。
doPostに追加する処理
| 順番 | 処理 |
|---|---|
| 1 | 文字コードをUTF-8に設定する |
| 2 | リクエストパラメータcustomerIdを取得する |
| 3 | リクエストパラメータloginPassを取得する |
| 4 | CustomerLoginインスタンスを作成する |
| 5 | CustomerLoginLogicのexecuteメソッドを呼び出す |
| 6 | ログイン成功時はセッションスコープにcustomerIdを保存する |
| 7 | ログイン成功時はcustomerLoginOK.jspへフォワードする |
| 8 | ログイン失敗時はCustomerLoginServletへリダイレクトする |
ログイン成功時と失敗時で処理を分岐させます。
成功時はセッションスコープを使います。
失敗時はセッションスコープに何も保存せず、ログイン画面へ戻します。
ログインに関するリクエストを処理するコントローラの例
ファイル名: CustomerLoginServlet.java
package servlet;
import java.io.IOException;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import model.CustomerLogin;
import model.CustomerLoginLogic;
@WebServlet("/CustomerLoginServlet")
public class CustomerLoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(
"WEB-INF/jsp/customerLogin.jsp");
dispatcher.forward(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// リクエストパラメータの取得
request.setCharacterEncoding("UTF-8");
String customerId = request.getParameter("customerId");
String loginPass = request.getParameter("loginPass");
// ログイン処理の実行
CustomerLogin login = new CustomerLogin(customerId, loginPass);
CustomerLoginLogic bo = new CustomerLoginLogic();
boolean result = bo.execute(login);
// ログイン処理の成否によって処理を分岐
if (result) {
// セッションスコープにユーザーIDを保存
HttpSession session = request.getSession();
session.setAttribute("customerId", customerId);
// ログイン成功画面へフォワード
RequestDispatcher dispatcher = request.getRequestDispatcher(
"WEB-INF/jsp/customerLoginOK.jsp");
dispatcher.forward(request, response);
} else {
// ログイン画面へリダイレクト
response.sendRedirect("CustomerLoginServlet");
return;
}
}
}このCustomerLoginServlet.javaが、ログイン処理を組み込んだ完成版です。
doGetは、ログイン画面を表示するために使います。
doPostは、ログインフォームから送信された値を受け取り、ログイン処理を行います。
request.setCharacterEncoding("UTF-8")は、POSTで送信された日本語などを正しく扱うために指定しています。
request.getParameter("customerId")でユーザーIDを取得します。
request.getParameter("loginPass")でパスワードを取得します。
その後、CustomerLoginインスタンスを作成し、CustomerLoginLogicのexecuteメソッドに渡します。
executeメソッドの戻り値がtrueであればログイン成功です。
ログイン成功時は、HttpSessionを取得し、customerIdという属性名でセッションスコープにユーザーIDを保存します。
その後、customerLoginOK.jspへフォワードします。
executeメソッドの戻り値がfalseであればログイン失敗です。
ログイン失敗時は、CustomerLoginServletへリダイレクトします。
リダイレクト後はdoGetが実行され、customerLogin.jspが再表示されます。
JSPファイルを仕上げる
最後に、ログイン成功画面のJSPを仕上げます。
仮作成したcustomerLoginOK.jspでは、[ユーザーID]というダミー文字列を表示していました。
完成版では、セッションスコープに保存されたcustomerIdを表示します。
ここでは、スクリプトレットやスクリプト式を使わず、EL式とJSTLのc:outを使います。
仕上げで変更する部分
| 変更前 | 変更後 |
|---|---|
| ようこそ[ユーザーID]さん | ようこそ<c:out value="${customerId}" />さん |
c:outを使うと、スコープに保存された値を安全に表示できます。
ログイン成功時にCustomerLoginServletがセッションスコープへcustomerIdを保存しているため、JSP側では${customerId}で取り出せます。
ログイン成功画面を出力するビューの例
ファイル名: customerLoginOK.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>みんなのショッピング</title>
</head>
<body>
<h1>ログイン成功</h1>
<p>ようこそ<c:out value="${customerId}" />さん</p>
<a href="MarketTopServlet">トップへ</a>
</body>
</html>customerLoginOK.jspは、src/main/webapp/WEB-INF/jspに配置します。
JSTLを使うために、taglibディレクティブを記述しています。
prefixにはcを指定し、uriにはjakarta.tags.coreを指定します。
c:outのvalue属性には${customerId}を指定します。
これにより、セッションスコープに保存されたcustomerIdが画面に表示されます。
ログイン成功後、ユーザーIDがmiyuであれば、画面には次のように表示されます。
ようこそmiyuさん
機能を動作確認する
すべてのプログラムとJSPを作成したら、ブラウザでログイン機能を確認します。
まず、MarketTopServletへアクセスします。
トップ画面が表示されたら、ログインをクリックします。

ログイン画面で、次の値を入力します。

ログイン成功の確認
| 入力項目 | 入力値 |
|---|---|
| ユーザーID | miyu |
| パスワード | 1234 |
ログインボタンをクリックし、ログイン成功画面にようこそmiyuさんと表示されれば成功です。

次に、パスワードを間違えてログイン失敗を確認します。
ログイン失敗の確認
| 入力項目 | 入力値 |
|---|---|
| ユーザーID | miyu |
| パスワード | 9999 |
この場合は、ログイン成功画面へ進まず、ログイン画面へ戻れば正常です。

動作確認で見るポイント
ログイン機能の動作確認では、画面が表示されるかだけでなく、処理の流れも確認します。
確認ポイント
| 確認項目 | 確認する内容 |
|---|---|
| トップ画面 | MarketTopServletからmarketTop.jspが表示されるか |
| ログイン画面 | ログインリンクからcustomerLogin.jspへ移動できるか |
| POST送信 | ログインボタンでCustomerLoginServletのdoPostが実行されるか |
| 成功時 | miyu、1234でログイン成功画面へ進むか |
| セッションスコープ | customerIdが保存され、JSPで表示されるか |
| 失敗時 | 間違ったパスワードでログイン画面へ戻るか |
| DAO | CUSTOMERSテーブルを正しく検索できているか |
| BO | trueとfalseを正しく返しているか |
エラーが出た場合は、どこまで動いているかを順番に確認します。
DAOとBOはすでにテスト済みなので、サーブレットやJSPを中心に確認できます。
たとえば、ログイン成功画面にユーザーIDが表示されない場合は、CustomerLoginServletでsession.setAttribute("customerId", customerId)が実行されているか、customerLoginOK.jspで${customerId}を指定しているかを確認します。
次の機能へ進むときの考え方
ログイン機能が完成したら、次の機能へ進めます。
たとえば、ユーザー登録機能を追加する場合は、今回作成した画面遷移図を拡張すると分かりやすくなります。
トップ画面のユーザー登録から、ユーザー登録画面へ進み、登録完了画面へ進む流れを追加します。
次に拡張できる機能
| 機能 | 内容 |
|---|---|
| ユーザー登録機能 | 新しい会員をCUSTOMERSテーブルに追加する |
| ログアウト機能 | セッションスコープを破棄してトップ画面へ戻る |
| 商品一覧機能 | 商品テーブルから商品情報を取得して表示する |
| マイページ機能 | ログイン中の会員情報を表示する |
1つの機能が完成したら、次の機能も同じように設計から始めます。
画面、サーブレット、JSP、Entity、BO、DAO、テーブルを整理し、少しずつ追加していきます。
ログイン機能で身に付けたいこと
今回作成したログイン機能では、Webアプリケーション開発でよく使う流れをまとめて確認しました。
今回扱った主な内容
| 内容 | 学習したこと |
|---|---|
| Entity | 入力情報やテーブルの1件分をJavaクラスで表す |
| DAO | データベース検索を専用クラスにまとめる |
| BO | アプリケーションの中心処理を担当する |
| 単体テスト | DAOやBOをサーブレットと連携する前に確認する |
| サーブレット | リクエストを受け取り、処理の流れを制御する |
| JSP | 画面を表示する |
| セッションスコープ | ログイン中のユーザーIDを保存する |
| EL式とJSTL | JSPでスコープの値を安全に表示する |
ログイン機能は、Webアプリケーションの入口になる大切な機能です。
画面だけでなく、データベース、DAO、BO、サーブレット、JSPが連携して動きます。
今回のように、モデルから作成してテストし、その後で画面遷移を作り、最後にサーブレットとJSPを仕上げる流れを身に付けると、ほかの機能も落ち着いて作りやすくなります。
まずは、みんなのショッピングのログイン機能を完成させ、次の機能へ少しずつ広げていきましょう。
