JSPとServletを使用した基本的なウェブアプリケーションの構築

開発環境

  • Apache NetBeans IDE 17
  • MySQL 8.0.31
  • JDK 1.8
  • Apache Tomcat 10.0.12

プロジェクト構造

実装コード

JSPファイル

signin.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>

<html>
<head>
  <title>ログイン</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      background-color: #f5f5f5;
      margin: 0;
      padding: 20px;
    }
    h1 {
      text-align: center;
      color: #333;
    }
    .login-form {
      max-width: 500px;
      margin: 30px auto;
      padding: 25px;
      border-radius: 8px;
      background-color: white;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    .form-group {
      margin-bottom: 20px;
    }
    .form-group label {
      display: block;
      margin-bottom: 8px;
      font-weight: bold;
    }
    .form-control {
      width: 100%;
      padding: 12px;
      border: 1px solid #ddd;
      border-radius: 4px;
      box-sizing: border-box;
      font-size: 16px;
    }
    .btn {
      background-color: #4285f4;
      color: white;
      border: none;
      padding: 12px 20px;
      border-radius: 4px;
      cursor: pointer;
      width: 100%;
      font-size: 16px;
      transition: background-color 0.3s;
    }
    .btn:hover {
      background-color: #3367d6;
    }
    .text-right {
      text-align: right;
      margin-top: 15px;
    }
    .text-right a {
      color: #4285f4;
      text-decoration: none;
    }
    .text-right a:hover {
      text-decoration: underline;
    }
  </style>
</head>
<body>
  <h1>ログインページ</h1>
  <form method="post" action="AuthenticationServlet" class="login-form">
    <div class="form-group">
      <label for="userId">ユーザー名:</label>
      <input type="text" placeholder="ユーザー名を入力" name="userId" class="form-control" required>
    </div>
    
    <div class="form-group">
      <label for="userPass">パスワード:</label>
      <input type="password" placeholder="パスワードを入力" name="userPass" class="form-control" required>
    </div>
    
    <button type="submit" class="btn">ログイン</button>
    
    <div class="text-right">
      <a href="signup.jsp">アカウントをお持ちでない方はこちら</a>
    </div>
  </form>
</body>
</html>

signup.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>

<html>
<head>
  <title>新規登録</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      background-color: #f5f5f5;
      margin: 0;
      padding: 20px;
    }
    h1 {
      text-align: center;
      color: #333;
    }
    .register-form {
      max-width: 500px;
      margin: 30px auto;
      padding: 25px;
      border-radius: 8px;
      background-color: white;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    .form-group {
      margin-bottom: 20px;
    }
    .form-group label {
      display: block;
      margin-bottom: 8px;
      font-weight: bold;
    }
    .form-control {
      width: 100%;
      padding: 12px;
      border: 1px solid #ddd;
      border-radius: 4px;
      box-sizing: border-box;
      font-size: 16px;
    }
    .btn {
      background-color: #34a853;
      color: white;
      border: none;
      padding: 12px 20px;
      border-radius: 4px;
      cursor: pointer;
      width: 100%;
      font-size: 16px;
      transition: background-color 0.3s;
    }
    .btn:hover {
      background-color: #2d8e47;
    }
    .text-right {
      text-align: right;
      margin-top: 15px;
    }
    .text-right a {
      color: #4285f4;
      text-decoration: none;
    }
    .text-right a:hover {
      text-decoration: underline;
    }
    .error {
      color: #ea4335;
      font-size: 14px;
      margin-top: 5px;
      display: none;
    }
    .success {
      color: #34a853;
      font-size: 14px;
      margin-top: 5px;
      display: none;
    }
    .password-requirements {
      font-size: 14px;
      color: #5f6368;
      margin-top: 5px;
    }
  </style>
</head>
<body>
  <h1>新規アカウント作成</h1>
  <form method="post" action="RegistrationServlet" class="register-form">
    <div class="form-group">
      <label for="newUserId">ユーザー名:</label>
      <input type="text" placeholder="ユーザー名を入力" name="newUserId" class="form-control" required>
    </div>
    
    <div class="form-group">
      <label for="newUserPass">パスワード:</label>
      <input type="password" placeholder="パスワードを入力" name="newUserPass" id="newUserPass" 
             pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[0-9a-zA-Z]{8,}$" class="form-control" required>
      <p class="password-requirements">パスワードは大文字、小文字、数字を含み、8文字以上である必要があります</p>
      <p class="error" id="passwordError">パスワードが要件を満たしていません</p>
    </div>
    
    <div class="form-group">
      <label for="confirmPass">パスワード(確認):</label>
      <input type="password" placeholder="パスワードを再入力" name="confirmPass" id="confirmPass" class="form-control" required>
      <p class="error" id="matchError">パスワードが一致しません</p>
      <p class="success" id="matchSuccess">パスワードが一致しました</p>
    </div>
    
    <button type="submit" class="btn">アカウント作成</button>
    
    <div class="text-right">
      <a href="signin.jsp">既にアカウントをお持ちの方はこちら</a>
    </div>
  </form>
  
  <script>
    // パスワードの検証
    const passwordField = document.getElementById("newUserPass");
    const confirmField = document.getElementById("confirmPass");
    const passwordError = document.getElementById("passwordError");
    const matchError = document.getElementById("matchError");
    const matchSuccess = document.getElementById("matchSuccess");

    function validatePasswordStrength() {
      const regex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[0-9a-zA-Z]{8,}$/;
      if (regex.test(passwordField.value)) {
        passwordError.style.display = "none";
        return true;
      } else {
        passwordError.style.display = "block";
        return false;
      }
    }

    function validatePasswordMatch() {
      if (passwordField.value === confirmField.value && passwordField.value !== "") {
        matchError.style.display = "none";
        matchSuccess.style.display = "block";
        return true;
      } else {
        matchError.style.display = "block";
        matchSuccess.style.display = "none";
        return false;
      }
    }

    passwordField.addEventListener("input", function() {
      validatePasswordStrength();
      if (confirmField.value !== "") {
        validatePasswordMatch();
      }
    });

    confirmField.addEventListener("input", validatePasswordMatch);
  </script>
</body>
</html>

dashboard.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>

<html>
<head>
  <meta charset="utf-8">
  <title>ペットクリニック</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      font-family: 'Helvetica Neue', Arial, sans-serif;
      line-height: 1.6;
      color: #333;
    }
    
    /* ナビゲーションバー */
    .navigation {
      background-color: #2c3e50;
      padding: 15px 0;
    }
    
    .nav-container {
      max-width: 1200px;
      margin: 0 auto;
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0 20px;
    }
    
    .logo {
      color: white;
      font-size: 24px;
      font-weight: bold;
      text-decoration: none;
    }
    
    .nav-links {
      display: flex;
    }
    
    .nav-links a {
      color: white;
      text-decoration: none;
      padding: 10px 15px;
      margin-left: 5px;
      border-radius: 4px;
      transition: background-color 0.3s;
    }
    
    .nav-links a:hover {
      background-color: #34495e;
    }
    
    /* ヒーローセクション */
    .hero {
      position: relative;
      height: 500px;
      overflow: hidden;
    }
    
    .hero-slide {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      opacity: 0;
      transition: opacity 1s ease-in-out;
    }
    
    .hero-slide.active {
      opacity: 1;
    }
    
    .hero-slide img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    
    .slide-indicators {
      position: absolute;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      display: flex;
      gap: 10px;
    }
    
    .indicator {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background-color: rgba(255, 255, 255, 0.5);
      cursor: pointer;
    }
    
    .indicator.active {
      background-color: white;
    }
    
    /* サービスセクション */
    .services {
      padding: 80px 20px;
      text-align: center;
      background-color: #f9f9f9;
    }
    
    .section-title {
      font-size: 36px;
      margin-bottom: 50px;
      color: #2c3e50;
    }
    
    .services-container {
      max-width: 1200px;
      margin: 0 auto;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 30px;
    }
    
    .service-card {
      flex: 1;
      min-width: 300px;
      max-width: 350px;
      background: white;
      border-radius: 8px;
      padding: 30px;
      box-shadow: 0 5px 15px rgba(0,0,0,0.05);
      transition: transform 0.3s, box-shadow 0.3s;
    }
    
    .service-card:hover {
      transform: translateY(-10px);
      box-shadow: 0 15px 30px rgba(0,0,0,0.1);
    }
    
    .service-icon {
      font-size: 40px;
      margin-bottom: 20px;
      color: #3498db;
    }
    
    .service-title {
      font-size: 24px;
      margin-bottom: 15px;
      color: #2c3e50;
    }
    
    .service-description {
      color: #7f8c8d;
      line-height: 1.8;
    }
    
    /* ニュースセクション */
    .news {
      padding: 80px 20px;
      background-color: #2c3e50;
      color: white;
    }
    
    .news-container {
      max-width: 1200px;
      margin: 0 auto;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 30px;
    }
    
    .news-card {
      flex: 1;
      min-width: 300px;
      max-width: 350px;
      background: white;
      border-radius: 8px;
      overflow: hidden;
      box-shadow: 0 5px 15px rgba(0,0,0,0.1);
    }
    
    .news-content {
      padding: 25px;
      color: #333;
    }
    
    .news-title {
      font-size: 20px;
      margin-bottom: 15px;
      color: #2c3e50;
    }
    
    .news-excerpt {
      color: #7f8c8d;
      line-height: 1.6;
    }
    
    /* 連絡先セクション */
    .contact {
      padding: 80px 20px;
      text-align: center;
      background-color: #f9f9f9;
    }
    
    .contact-info {
      max-width: 800px;
      margin: 0 auto;
    }
    
    .contact-item {
      margin-bottom: 20px;
      font-size: 18px;
    }
    
    .contact-item strong {
      color: #2c3e50;
      display: inline-block;
      width: 100px;
    }
    
    /* フッター */
    footer {
      background-color: #2c3e50;
      color: white;
      text-align: center;
      padding: 20px;
    }
  </style>
</head>
<body>
  <!-- ナビゲーションバー -->
  <nav class="navigation">
    <div class="nav-container">
      <a href="#" class="logo">ペットクリニック</a>
      <div class="nav-links">
        <a href="#">ホーム</a>
        <a href="#">サービス</a>
        <a href="#">スタッフ</a>
        <a href="#">お知らせ</a>
        <a href="#">お問い合わせ</a>
      </div>
    </div>
  </nav>

  <!-- ヒーローセクション -->
  <div class="hero">
    <div class="hero-slide active">
      <img src="https://picsum.photos/1200/500/?random=1" alt="ペットケア">
    </div>
    <div class="hero-slide">
      <img src="https://picsum.photos/1200/500/?random=2" alt="ペット診療">
    </div>
    <div class="hero-slide">
      <img src="https://picsum.photos/1200/500/?random=3" alt="ペット健康">
    </div>
    <div class="slide-indicators">
      <span class="indicator active" onclick="currentSlide(1)"></span>
      <span class="indicator" onclick="currentSlide(2)"></span>
      <span class="indicator" onclick="currentSlide(3)"></span>
    </div>
  </div>

  <!-- サービスセクション -->
  <section class="services">
    <h2 class="section-title">当院のサービス</h2>
    <div class="services-container">
      <div class="service-card">
        <div class="service-icon">💉</div>
        <h3 class="service-title">ワクチン接種</h3>
        <p class="service-description">ペットのための各種ワクチン接種と予防医療サービスを提供し、健康な成長をサポートします。</p>
      </div>
      <div class="service-card">
        <div class="service-icon">🏥</div>
        <h3 class="service-title">外科手術</h3>
        <p class="service-description">経験豊富な外科医チームによる、ペットのための各種手術を専門に行っています。</p>
      </div>
      <div class="service-card">
        <div class="service-icon">🔬</div>
        <h3 class="service-title">精密検査</h3>
        <p class="service-description">最新の診断機器を活用し、迅速かつ正確な診断で最適な治療計画を立てます。</p>
      </div>
    </div>
  </section>

  <!-- ニュースセクション -->
  <section class="news">
    <h2 class="section-title">最新情報</h2>
    <div class="news-container">
      <div class="news-card">
        <div class="news-content">
          <h3 class="news-title">ペットの正しいシャンプーの方法</h3>
          <p class="news-excerpt">ペットの日常ケアにおいてシャンプーは重要ですが、正しい方法で行わないと健康に悪影響を及ぼす可能性があります。</p>
        </div>
      </div>
      <div class="news-card">
        <div class="news-content">
          <h3 class="news-title">ペットの風邪予防法</h3>
          <p class="news-excerpt">寒い季節にはペットも風邪を引きやすくなります。風邪を引いたペットの状態は深刻化することがあるため、予防が重要です。</p>
        </div>
      </div>
      <div class="news-card">
        <div class="news-content">
          <h3 class="news-title">猫用フードの選び方</h3>
          <p class="news-excerpt">猫用フードの品質はペットの健康に直接影響するため、適切なフードを選ぶことが非常に重要です。</p>
        </div>
      </div>
    </div>
  </section>

  <!-- 連絡先セクション -->
  <section class="contact">
    <h2 class="section-title">お問い合わせ</h2>
    <div class="contact-info">
      <div class="contact-item">
        <strong>住所</strong>: 〇〇県〇〇市〇〇区〇〇町〇〇番地
      </div>
      <div class="contact-item">
        <strong>電話番号</strong>: 0123-456-7890
      </div>
      <div class="contact-item">
        <strong>メール</strong>: info@pet-clinic.jp
      </div>
    </div>
  </section>

  <!-- フッター -->
  <footer>
    <p>© 2023 ペットクリニック. All rights reserved.</p>
  </footer>

  <script>
    // スライドショーの制御
    let slideIndex = 1;
    
    function currentSlide(n) {
      showSlide(slideIndex = n);
    }
    
    function showSlide(n) {
      const slides = document.getElementsByClassName("hero-slide");
      const indicators = document.getElementsByClassName("indicator");
      
      if (n > slides.length) {slideIndex = 1}
      if (n < 1) {slideIndex = slides.length}
      
      for (let i = 0; i < slides.length; i++) {
        slides[i].classList.remove("active");
      }
      
      for (let i = 0; i < indicators.length; i++) {
        indicators[i].classList.remove("active");
      }
      
      slides[slideIndex-1].classList.add("active");
      indicators[slideIndex-1].classList.add("active");
    }
    
    // 自動スライド
    setInterval(function() {
      slideIndex++;
      if (slideIndex > 3) {
        slideIndex = 1;
      }
      showSlide(slideIndex);
    }, 5000);
  </script>
</body>
</html>

web.xmlの設定

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" 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_3_1.xsd">
    <servlet>
        <servlet-name>AuthenticationServlet</servlet-name>
        <servlet-class>com.clinic.AuthServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>RegistrationServlet</servlet-name>
        <servlet-class>com.clinic.RegServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AuthenticationServlet</servlet-name>
        <url-pattern>/AuthenticationServlet</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>RegistrationServlet</servlet-name>
        <url-pattern>/RegistrationServlet</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

Javaサーブレットコード

AuthServlet.java

package com.clinic;

import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class AuthServlet extends HttpServlet {
  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse res) 
  throws ServletException, IOException {
    String userIdentifier = req.getParameter("userId");
    String userCredentials = req.getParameter("userPass");

    // ユーザー認証の実行
    boolean authResult = verifyCredentials(userIdentifier, userCredentials);

    if (authResult) {
      // 認証成功、セッションにユーザー情報を保存
      HttpSession userSession = req.getSession(true);
      userSession.setAttribute("currentUser", userIdentifier);
      
      // ダッシュボードページへリダイレクト
      res.sendRedirect("dashboard.jsp");
    } else {
      // 認証失敗、ログインページへ戻る
      res.sendRedirect("signin.jsp");
    }
  }

  // ユーザー認証ロジック
  private boolean verifyCredentials(String loginId, String loginPass) {
    boolean isValidUser = false;
    
    try {
        // データベースドライバのロード
        Class.forName("com.mysql.jdbc.Driver");

        // データベース接続の確立
        String dbUrl = "jdbc:mysql://localhost:3306/pet_clinic_db";
        String dbUser = "admin";
        String dbPassword = "password123";
        Connection dbConnection = DriverManager.getConnection(dbUrl, dbUser, dbPassword);

        // SQLクエリの準備
        String query = "SELECT * FROM users WHERE user_id = ? AND user_pass = ?";
        PreparedStatement prepStatement = dbConnection.prepareStatement(query);
        prepStatement.setString(1, loginId);
        prepStatement.setString(2, loginPass);

        // クエリの実行
        ResultSet queryResult = prepStatement.executeQuery();

        // 結果の確認
        if (queryResult.next()) {
            isValidUser = true;
        }

        // リソースの解放
        queryResult.close();
        prepStatement.close();
        dbConnection.close();
        
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    }
    
    return isValidUser;
  }
}

RegServlet.java

package com.clinic;

import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class RegServlet extends HttpServlet {
  public void doPost(HttpServletRequest req, HttpServletResponse res) 
  throws ServletException, IOException {
    String newUserId = req.getParameter("newUserId");
    String newUserPass = req.getParameter("newUserPass");

    // ユーザー登録ロジックの実行
    boolean registrationResult = createUserAccount(newUserId, newUserPass);

    if (registrationResult) {
      // 登録成功、セッションにユーザー情報を保存
      HttpSession userSession = req.getSession(true);
      userSession.setAttribute("currentUser", newUserId);

      // ダッシュボードページへリダイレクト
      res.sendRedirect("dashboard.jsp");
    } else {
      // 登録失敗、登録ページへ戻る
      res.sendRedirect("signup.jsp");
    }
  }

  // ユーザー登録ロジック
  private boolean createUserAccount(String userId, String userPass) {
      boolean isCreated = false;

      try {
          // データベースドライバのロード
          Class.forName("com.mysql.jdbc.Driver");

          // データベース接続の確立
          String dbUrl = "jdbc:mysql://localhost:3306/pet_clinic_db";
          String dbUser = "admin";
          String dbPassword = "password123";
          
          try (Connection dbConnection = DriverManager.getConnection(dbUrl, dbUser, dbPassword)) {
              // ユーザーが既に存在するか確認
              String checkQuery = "SELECT user_id FROM users WHERE user_id = ?";
              try (PreparedStatement checkStmt = dbConnection.prepareStatement(checkQuery)) {
                  checkStmt.setString(1, userId);
                  ResultSet checkResult = checkStmt.executeQuery();
                  
                  if (checkResult.next()) {
                      // ユーザーが既に存在する場合
                      checkResult.close();
                      return false;
                  }
                  checkResult.close();
              }
              
              // 新規ユーザーの登録
              String insertQuery = "INSERT INTO users (user_id, user_pass) VALUES (?, ?)";
              try (PreparedStatement insertStmt = dbConnection.prepareStatement(insertQuery)) {
                  insertStmt.setString(1, userId);
                  insertStmt.setString(2, userPass);
                  
                  int rowsAffected = insertStmt.executeUpdate();
                  
                  if (rowsAffected > 0) {
                      isCreated = true;
                  }
              }
          }
      } catch (ClassNotFoundException | SQLException e) {
          System.out.println("データベースエラー: " + e.getMessage());
          e.printStackTrace();
      }

      return isCreated;
  }
}

データベース設定

MySQLデータベースの作成

-- 1. データベースの作成
CREATE DATABASE pet_clinic_db;

-- 2. データベースの選択
USE pet_clinic_db;
 
-- 3. usersテーブルの作成
CREATE TABLE users (
       user_id VARCHAR(50) NOT NULL,
       user_pass VARCHAR(100) NOT NULL,
       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
       PRIMARY KEY (user_id)
    );

実装された機能

  • ユーザーログイン(データベースとの認証連携)
  • 新規ユーザー登録(パスワード強度要件、パスワード確認機能付き)
  • ペットクリニックのダッシュボードページ

タグ: JSP Servlet MySQL Java Webアプリケーション

6月24日 18:46 投稿