はじめに 🧭
本記事では、Java初心者がWebアプリケーション開発の基礎を学べるよう、ログイン・ログアウト機能とデータベース連携を持つシンプルなWebアプリケーションの作成手順を解説します。
完成すると以下の機能を持つアプリケーションが動作します:
- ユーザーログイン・ログアウト
- セッション管理によるアクセス制御
- データベースからのユーザー一覧表示
- 基本的なセキュリティ対策
開発環境 🧰
- Java: 11以上
- IDE: Eclipse IDE for Enterprise Java and Web Developers
- Webサーバー: Apache Tomcat 9.0
- データベース: MariaDB (MySQL互換)
- ブラウザ: Chrome, Firefox等
完成イメージ 🖼️
- ログイン画面: ユーザー名・パスワード入力
- ダッシュボード: ログイン後のメイン画面でユーザー一覧表示
- セッション管理: 未ログイン時は自動的にログイン画面にリダイレクト
Step 1: プロジェクト作成 🏗️
1-1. Eclipseでの新規プロジェクト作成
-
Eclipseを起動
-
File → New → Dynamic Web Project
-
以下を設定:
-
Project name:
SimpleWebApp - Target runtime: Apache Tomcat v9.0
- Dynamic web module version: 4.0
-
Project name:
-
Next → Next → Generate web.xml deployment descriptor にチェック
-
Finish
1-2. パッケージ構成
src/main/java配下に以下のパッケージを作成:
com.example.simple # メインクラス
com.example.simple.config # 設定クラス
Step 2: データベース環境構築 🗄️
2-1. MariaDBでのデータベース・テーブル作成
XAMPPのMySQLを立ち上げて→http://localhost/phpmyadmin/
MariaDBクライアントで以下のSQLを実行:
-- データベース作成
CREATE DATABASE simple_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE simple_app;
-- ユーザーテーブル作成
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- テストユーザー追加
INSERT INTO users (username, password) VALUES
('admin', 'password'),
('user1', 'test123'),
('demo', 'demo');
-- 確認
SELECT * FROM users;
2-2. JDBCドライバーとJSTLライブラリの配置
以下のJARファイルをダウンロードし、プロジェクトのwebapp/WEB-INF/lib/フォルダに配置:
- mysql-connector-j-8.0.33.jar (MySQL公式サイトから)
- jstl-1.2.jar (Maven Central等から)
- standard-1.1.2.jar (JSTLの実装ライブラリ)
配置後、Eclipse上でプロジェクトを右クリック → Refresh
Step 3: 設定ファイルの作成 ⚙️
3-1. web.xml
webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>SimpleWebApp</display-name>
<!-- 文字エンコーディングフィルター -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.example.simple.config.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- デフォルトページ -->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
3-2. 文字エンコーディングフィルター
com.example.simple.config.CharacterEncodingFilter.java
package com.example.simple.config;
import java.io.IOException;
import javax.servlet.*;
public class CharacterEncodingFilter implements Filter {
private String encoding = "UTF-8";
private boolean forceEncoding = false;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String encodingParam = filterConfig.getInitParameter("encoding");
if (encodingParam != null) {
this.encoding = encodingParam;
}
String forceEncodingParam = filterConfig.getInitParameter("forceEncoding");
if (forceEncodingParam != null) {
this.forceEncoding = Boolean.parseBoolean(forceEncodingParam);
}
System.out.println("[CharacterEncodingFilter] Initialized with encoding: " + encoding);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// リクエストの文字エンコーディング設定
if (forceEncoding || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
// レスポンスの文字エンコーディング設定
response.setCharacterEncoding(encoding);
response.setContentType("text/html; charset=" + encoding);
chain.doFilter(request, response);
}
@Override
public void destroy() {
// クリーンアップ処理
}
}
Step 4: データアクセス層の実装 🧩
4-1. Userエンティティクラス
com.example.simple.User.java
package com.example.simple;
public class User {
private int id;
private String username;
private String password;
// デフォルトコンストラクタ
public User() {}
// コンストラクタ
public User(String username, String password) {
this.username = username;
this.password = password;
}
// ゲッター・セッター
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
4-2. UserDAOクラス(データアクセスオブジェクト)
com.example.simple.UserDAO.java
package com.example.simple;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class UserDAO {
// データベース接続情報(環境に合わせて変更)
private static final String URL = "jdbc:mysql://localhost:3306/simple_app?useSSL=false&serverTimezone=Asia/Tokyo&characterEncoding=UTF-8";
private static final String DB_USER = "root";
private static final String DB_PASSWORD = ""; // 環境に合わせて変更
// JDBCドライバーの読み込み
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("MySQL JDBC driver loaded successfully");
} catch (ClassNotFoundException e) {
System.err.println("MySQL JDBC driver not found: " + e.getMessage());
throw new RuntimeException("MySQL driver not found in classpath", e);
}
}
// データベース接続取得
private Connection getConnection() throws SQLException {
try {
return DriverManager.getConnection(URL, DB_USER, DB_PASSWORD);
} catch (SQLException e) {
System.err.println("Database connection failed: " + e.getMessage());
throw e;
}
}
/**
* ユーザー認証
* @param username ユーザー名
* @param password パスワード
* @return 認証成功時はUserオブジェクト、失敗時はnull
*/
public User authenticate(String username, String password) {
String sql = "SELECT id, username FROM users WHERE username = ? AND password = ?";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, username);
ps.setString(2, password);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
return user;
}
}
} catch (SQLException e) {
System.err.println("Authentication error: " + e.getMessage());
e.printStackTrace();
}
return null;
}
/**
* 全ユーザー取得
* @return ユーザーリスト
*/
public List<User> getAllUsers() {
List<User> users = new ArrayList<>();
String sql = "SELECT id, username FROM users ORDER BY id";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
users.add(user);
}
} catch (SQLException e) {
System.err.println("Get all users error: " + e.getMessage());
e.printStackTrace();
}
return users;
}
}
Step 5: サーブレットの実装 🧠
5-1. ログインサーブレット
com.example.simple.LoginServlet.java
package com.example.simple;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
// GET: ログインページ表示
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.getRequestDispatcher("login.jsp").forward(request, response);
}
// POST: ログイン処理
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// 入力値チェック
if (username == null || username.trim().isEmpty() ||
password == null || password.trim().isEmpty()) {
request.setAttribute("error", "ユーザー名とパスワードを入力してください");
request.getRequestDispatcher("login.jsp").forward(request, response);
return;
}
// データベースでの認証
UserDAO userDAO = new UserDAO();
User user = userDAO.authenticate(username.trim(), password);
if (user != null) {
// ログイン成功: セッションにユーザー情報を保存
HttpSession session = request.getSession();
session.setAttribute("user", user);
session.setMaxInactiveInterval(30 * 60); // 30分でタイムアウト
System.out.println("Login successful: " + user.getUsername());
response.sendRedirect("dashboard");
} else {
// ログイン失敗
System.out.println("Login failed for username: " + username);
request.setAttribute("error", "ユーザー名またはパスワードが間違っています");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
}
}
5-2. ダッシュボードサーブレット
com.example.simple.DashboardServlet.java
package com.example.simple;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/dashboard")
public class DashboardServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// セッション認証チェック
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("user") == null) {
System.out.println("Unauthorized access attempt to dashboard");
response.sendRedirect("login");
return;
}
// ログイン済みユーザーの処理
User currentUser = (User) session.getAttribute("user");
System.out.println("Dashboard accessed by: " + currentUser.getUsername());
// ユーザー一覧をデータベースから取得
UserDAO userDAO = new UserDAO();
List<User> users = userDAO.getAllUsers();
// JSPに渡すためのリクエスト属性設定
request.setAttribute("users", users);
request.setAttribute("currentUser", currentUser);
// ダッシュボードページに転送
request.getRequestDispatcher("dashboard.jsp").forward(request, response);
}
}
5-3. ログアウトサーブレット
com.example.simple.LogoutServlet.java
package com.example.simple;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// セッション取得と無効化
HttpSession session = request.getSession(false);
if (session != null) {
User user = (User) session.getAttribute("user");
if (user != null) {
System.out.println("Logout: " + user.getUsername());
}
session.invalidate(); // セッション破棄
}
// ログインページにリダイレクト
response.sendRedirect("login");
}
}
Step 6: JSPページの実装 🎨
6-1. ログインページ
webapp/login.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ログイン - Simple Web App</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f5f5;
margin: 0;
padding: 50px 20px;
}
.login-container {
max-width: 400px;
margin: 0 auto;
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: 500;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
input[type="text"]:focus, input[type="password"]:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
}
.btn {
width: 100%;
padding: 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s;
}
.btn:hover {
background-color: #0056b3;
}
.error {
color: #dc3545;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
}
.demo-info {
margin-top: 20px;
padding: 15px;
background-color: #e7f3ff;
border-radius: 4px;
font-size: 14px;
}
.demo-info h4 {
margin: 0 0 10px 0;
color: #0056b3;
}
</style>
</head>
<body>
<div class="login-container">
<h2>ログイン</h2>
<!-- エラーメッセージ表示 -->
<% if (request.getAttribute("error") != null) { %>
<div class="error">
<%= request.getAttribute("error") %>
</div>
<% } %>
<!-- ログインフォーム -->
<form method="post" action="login">
<div class="form-group">
<label for="username">ユーザー名</label>
<input type="text" id="username" name="username"
value="<%= request.getParameter("username") != null ? request.getParameter("username") : "" %>"
placeholder="ユーザー名を入力" required>
</div>
<div class="form-group">
<label for="password">パスワード</label>
<input type="password" id="password" name="password"
placeholder="パスワードを入力" required>
</div>
<button type="submit" class="btn">ログイン</button>
</form>
<!-- デモユーザー情報 -->
<div class="demo-info">
<h4>テストユーザー</h4>
<p><strong>ユーザー名:</strong> admin<br>
<strong>パスワード:</strong> password</p>
<p><strong>ユーザー名:</strong> user1<br>
<strong>パスワード:</strong> test123</p>
</div>
</div>
</body>
</html>
6-2. ダッシュボードページ
webapp/dashboard.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="java.util.List" %>
<%@ page import="com.example.simple.User" %>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ダッシュボード - Simple Web App</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
}
.header {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
margin: 0;
color: #333;
}
.user-info {
display: flex;
align-items: center;
gap: 15px;
}
.welcome-text {
color: #666;
}
.logout-btn {
background-color: #dc3545;
color: white;
padding: 8px 16px;
text-decoration: none;
border-radius: 4px;
font-size: 14px;
transition: background-color 0.2s;
}
.logout-btn:hover {
background-color: #c82333;
}
.content {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.section-title {
color: #333;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #007bff;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f8f9fa;
font-weight: 600;
color: #495057;
border-bottom: 2px solid #dee2e6;
}
tr:hover {
background-color: #f8f9fa;
}
.user-count {
color: #6c757d;
font-size: 14px;
margin-bottom: 15px;
}
.current-user {
background-color: #e3f2fd;
}
</style>
</head>
<body>
<div class="header">
<h1>ダッシュボード</h1>
<div class="user-info">
<span class="welcome-text">
ようこそ、<strong><%= ((User)session.getAttribute("user")).getUsername() %></strong>さん
</span>
<a href="logout" class="logout-btn">ログアウト</a>
</div>
</div>
<div class="content">
<h2 class="section-title">ユーザー管理</h2>
<%
@SuppressWarnings("unchecked")
List<User> users = (List<User>) request.getAttribute("users");
User currentUser = (User) session.getAttribute("user");
%>
<div class="user-count">
登録ユーザー数: <%= users != null ? users.size() : 0 %>名
</div>
<table>
<thead>
<tr>
<th>ユーザーID</th>
<th>ユーザー名</th>
<th>ステータス</th>
</tr>
</thead>
<tbody>
<%
if (users != null) {
for (User user : users) {
boolean isCurrentUser = currentUser != null &&
currentUser.getId() == user.getId();
%>
<tr <%= isCurrentUser ? "class=\"current-user\"" : "" %>>
<td><%= user.getId() %></td>
<td><%= user.getUsername() %></td>
<td>
<%= isCurrentUser ? "ログイン中" : "オフライン" %>
</td>
</tr>
<%
}
} else {
%>
<tr>
<td colspan="3" style="text-align: center; color: #666;">
ユーザーデータがありません
</td>
</tr>
<%
}
%>
</tbody>
</table>
</div>
</body>
</html>
Step 7: 動作確認とテスト ✅
7-1. アプリケーションの起動
- Tomcatサーバー起動: Eclipse下部の「Servers」タブからTomcatを起動
- プロジェクトデプロイ: プロジェクトを右クリック → Run As → Run on Server
-
ブラウザアクセス:
http://localhost:8080/SimpleWebApp
7-2. 動作テストシナリオ
テスト1: ログインエラーテスト
- 間違ったユーザー名・パスワードでログイン試行
- エラーメッセージが表示されることを確認
テスト2: 正常ログインテスト
-
admin/passwordでログイン - ダッシュボードページに遷移することを確認
- ユーザー一覧が表示されることを確認
テスト3: セッション管理テスト
- 新しいブラウザタブを開く
-
http://localhost:8080/SimpleWebApp/dashboardに直接アクセス - ログインページにリダイレクトされることを確認
テスト4: ログアウトテスト
- ダッシュボードで「ログアウト」をクリック
- ログインページに戻ることを確認
トラブルシューティング 🧯
よくあるエラーと解決策
1. ClassNotFoundException: com.mysql.cj.jdbc.Driver
原因: JDBCドライバーJARファイルが配置されていない
解決: mysql-connector-j-8.x.x.jarをWEB-INF/libに配置
2. NoClassDefFoundError: Could not initialize class UserDAO
原因: 静的初期化でJDBCドライバーロードに失敗
解決: JARファイルの配置とプロジェクトのリフレッシュ
3. 絶対URI: [http://java.sun.com/jsp/jstl/core] は解決できません
原因: JSTLライブラリが不足
解決: jstl-1.2.jarとstandard-1.1.2.jarをWEB-INF/libに配置
4. 文字化け
原因: 文字エンコーディング設定の不備
解決: CharacterEncodingFilterが正しく動作しているか確認
まとめ 📝
本カリキュラムで学習した内容:
技術要素
- サーブレット: HTTPリクエストの処理
- JSP: 動的Webページの生成
- JDBC: データベース接続とSQL実行
- セッション管理: ユーザー状態の保持
- フィルター: 共通処理の実装
セキュリティ対策
- 入力値検証
- SQLインジェクション対策(PreparedStatement使用)
- セッションベースの認証・認可
実践的スキル
- MVCアーキテクチャの基礎理解
- エラーハンドリング
- デバッグ手法
次のステップ ⏭️
この基礎アプリケーションをベースに、以下の機能を追加してスキルアップを図ることができます:
- CRUD機能: ユーザーの追加・編集・削除
- バリデーション強化: フォームバリデーション
- 検索・ソート機能: ユーザー検索とソート
- ページング: 大量データの分割表示
- FrontControllerパターン: より洗練されたアーキテクチャ
実際のWebアプリケーション開発では、このような基礎から段階的に機能を拡張していくことで、堅牢で保守性の高いアプリケーションを構築できます。
参考リンク 🔗
補足: 本格的な開発への移行 🌱
このカリキュラムで学んだ基礎技術は、Spring BootやStruts等のWebフレームワークを学ぶ際の土台となります。フレームワークを使用することで:
- 設定の簡素化: アノテーションベースの設定
- DI(依存性注入): オブジェクト管理の自動化
- ORM: データベースアクセスの抽象化
- セキュリティ: 認証・認可の標準化
まずは今回の基礎をしっかり理解してから、次のステップに進むことをお勧めします。
完成したサンプルコードは GitHub 等で公開し、実際に動作するデモ環境を構築して学習効果を高めましょう。
(๑•̀ㅂ•́)و✧ Happy Coding!