1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【超初心者向け】Java Webアプリケーション開発入門 - ログイン機能付きユーザー管理システム

Last updated at Posted at 2025-09-17

はじめに 🧭

本記事では、Java初心者がWebアプリケーション開発の基礎を学べるよう、ログイン・ログアウト機能データベース連携を持つシンプルなWebアプリケーションの作成手順を解説します。

完成すると以下の機能を持つアプリケーションが動作します:

  • ユーザーログイン・ログアウト
  • セッション管理によるアクセス制御
  • データベースからのユーザー一覧表示
  • 基本的なセキュリティ対策

開発環境 🧰

  • Java: 11以上
  • IDE: Eclipse IDE for Enterprise Java and Web Developers
  • Webサーバー: Apache Tomcat 9.0
  • データベース: MariaDB (MySQL互換)
  • ブラウザ: Chrome, Firefox等

完成イメージ 🖼️

  1. ログイン画面: ユーザー名・パスワード入力
  2. ダッシュボード: ログイン後のメイン画面でユーザー一覧表示
  3. セッション管理: 未ログイン時は自動的にログイン画面にリダイレクト

Step 1: プロジェクト作成 🏗️

1-1. Eclipseでの新規プロジェクト作成

  1. Eclipseを起動

  2. FileNewDynamic Web Project

  3. 以下を設定:

    • Project name: SimpleWebApp
    • Target runtime: Apache Tomcat v9.0
    • Dynamic web module version: 4.0
  4. NextNextGenerate web.xml deployment descriptor にチェック

  5. 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/フォルダに配置:

  1. mysql-connector-j-8.0.33.jar (MySQL公式サイトから)
  2. jstl-1.2.jar (Maven Central等から)
  3. 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. アプリケーションの起動

  1. Tomcatサーバー起動: Eclipse下部の「Servers」タブからTomcatを起動
  2. プロジェクトデプロイ: プロジェクトを右クリック → Run AsRun on Server
  3. ブラウザアクセス: http://localhost:8080/SimpleWebApp

7-2. 動作テストシナリオ

テスト1: ログインエラーテスト

  1. 間違ったユーザー名・パスワードでログイン試行
  2. エラーメッセージが表示されることを確認

テスト2: 正常ログインテスト

  1. admin / password でログイン
  2. ダッシュボードページに遷移することを確認
  3. ユーザー一覧が表示されることを確認

テスト3: セッション管理テスト

  1. 新しいブラウザタブを開く
  2. http://localhost:8080/SimpleWebApp/dashboard に直接アクセス
  3. ログインページにリダイレクトされることを確認

テスト4: ログアウトテスト

  1. ダッシュボードで「ログアウト」をクリック
  2. ログインページに戻ることを確認

トラブルシューティング 🧯

よくあるエラーと解決策

1. ClassNotFoundException: com.mysql.cj.jdbc.Driver

原因: JDBCドライバーJARファイルが配置されていない
解決: mysql-connector-j-8.x.x.jarWEB-INF/libに配置

2. NoClassDefFoundError: Could not initialize class UserDAO

原因: 静的初期化でJDBCドライバーロードに失敗
解決: JARファイルの配置とプロジェクトのリフレッシュ

3. 絶対URI: [http://java.sun.com/jsp/jstl/core] は解決できません

原因: JSTLライブラリが不足
解決: jstl-1.2.jarstandard-1.1.2.jarWEB-INF/libに配置

4. 文字化け

原因: 文字エンコーディング設定の不備
解決: CharacterEncodingFilterが正しく動作しているか確認


まとめ 📝

本カリキュラムで学習した内容:

技術要素

  • サーブレット: HTTPリクエストの処理
  • JSP: 動的Webページの生成
  • JDBC: データベース接続とSQL実行
  • セッション管理: ユーザー状態の保持
  • フィルター: 共通処理の実装

セキュリティ対策

  • 入力値検証
  • SQLインジェクション対策(PreparedStatement使用)
  • セッションベースの認証・認可

実践的スキル

  • MVCアーキテクチャの基礎理解
  • エラーハンドリング
  • デバッグ手法

次のステップ ⏭️

この基礎アプリケーションをベースに、以下の機能を追加してスキルアップを図ることができます:

  1. CRUD機能: ユーザーの追加・編集・削除
  2. バリデーション強化: フォームバリデーション
  3. 検索・ソート機能: ユーザー検索とソート
  4. ページング: 大量データの分割表示
  5. FrontControllerパターン: より洗練されたアーキテクチャ

実際のWebアプリケーション開発では、このような基礎から段階的に機能を拡張していくことで、堅牢で保守性の高いアプリケーションを構築できます。

参考リンク 🔗

補足: 本格的な開発への移行 🌱

このカリキュラムで学んだ基礎技術は、Spring BootやStruts等のWebフレームワークを学ぶ際の土台となります。フレームワークを使用することで:

  • 設定の簡素化: アノテーションベースの設定
  • DI(依存性注入): オブジェクト管理の自動化
  • ORM: データベースアクセスの抽象化
  • セキュリティ: 認証・認可の標準化

まずは今回の基礎をしっかり理解してから、次のステップに進むことをお勧めします。


完成したサンプルコードは GitHub 等で公開し、実際に動作するデモ環境を構築して学習効果を高めましょう。
(๑•̀ㅂ•́)و✧ Happy Coding!

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?