この記事はJava学習シリーズ「課題1」です。まだ環境が整っていない方は Java環境構築ガイド を先に読んでください。
この記事で作るもの
ブラウザで動くパスワード強度チェッカーWebアプリです。HTMLフォームにパスワードを入力すると、Spring Boot APIが強度を判定して結果を表示します。
APIとして使う場合(curl):
curl -X POST http://localhost:8080/api/check-password \
-H "Content-Type: application/json" \
-d '{"password": "hello"}'
{
"score": 1,
"strength": "弱い",
"stars": "★☆☆☆☆",
"advice": [
"8文字以上にしてください(現在: 5文字)",
"大文字(A-Z)を含めてください",
"数字(0-9)を含めてください",
"記号(!@#$%^&*)を含めてください"
]
}
ブラウザで使う場合: http://localhost:8080/ を開くとHTMLフォームが表示されます。
この記事で学べること
- Spring Bootの基本構造(最初のWebアプリ)
- アノテーション(
@RestController,@PostMapping,@RequestBody) - 変数と型(
int,String,boolean) - 条件分岐(
if/else if/else) - 正規表現(文字パターンの検索)
- record(シンプルなDTOクラス)
1. 要件定義:何を作るかを整理する
コードを書く前に「このAPIが何をしなければならないか」を整理します。
| # | 要件(やること) | 理由 |
|---|---|---|
| 1 | POST /api/check-password でパスワードを受け取る | HTTPリクエストで入力を受け取るWebサービスにするため |
| 2 | 8文字以上かどうかをチェックする | 短すぎると総当たり攻撃に弱い |
| 3 | 大文字・小文字・数字・記号が含まれるかチェックする | 文字の多様性がパスワード強度を上げる |
| 4 | 達成条件数をスコアにして★で表示する | 「どれくらい強いか」を視覚的に伝える |
| 5 | 未達成の条件をアドバイスとして返す | ユーザーが何を改善すればいいかわかる |
| 6 | ブラウザから使えるHTMLフォームを提供する | APIだけでなくブラウザで動く画面も用意してポートフォリオとして映えるようにする |
2. Spring Initializrでプロジェクト作成
Spring Bootのプロジェクトは start.spring.io で雛形を生成します。
Project: Maven
Language: Java
Spring Boot: 3.3.x
Group: com.example
Artifact: password-checker
Name: password-checker
Java: 21
Dependencies(「ADD DEPENDENCIES」をクリックして追加):
✓ Spring Web ← HTTPリクエストを受け取るために必要
✓ Validation ← リクエストの入力チェックに使う
「GENERATE」でZIPをダウンロード → 解凍 → IntelliJで「File → Open」。
生成されるプロジェクト構造:
password-checker/
├── pom.xml
└── src/main/
├── java/com/example/passwordchecker/
│ └── PasswordCheckerApplication.java ← Spring Bootの起動クラス
└── resources/
├── application.properties
└── static/ ← ここにindex.htmlを置く
3. Spring Bootの基本構造を理解する
Spring Bootとは? Javaで「WebからHTTPで呼び出せるサービス(REST API)」を作るための最も普及したフレームワークです。HTTPサーバー(Tomcat)が内蔵されており、コードを書いたらすぐにブラウザやcurlから呼び出せます。クラウドワークスのJava案件のほぼすべてでSpring Bootが使われています。
Spring Bootで必要な主要アノテーションを先に把握しておきます。
| アノテーション | 意味 |
|---|---|
@RestController | このクラスはHTTPリクエストを受け取り、JSONを返すControllerだという宣言 |
@RequestMapping("/api") | このControllerは /api 配下のURLを担当する |
@PostMapping("/check-password") | POST /api/check-password をこのメソッドが処理する |
@RequestBody | HTTPリクエストのJSON本体をJavaオブジェクトに変換して受け取る |
アノテーション(Annotation)とは?
@で始まる「ラベル(マーカー)」です。クラスやメソッドに付けると、Springフレームワークがそれを読み取って自動的に処理を追加してくれます。たとえば@RestControllerを付けるだけで「HTTPを受け取ってJSONを返す」設定が完了します。
4. まずHello Worldエンドポイントを作る
src/main/java/com/example/passwordchecker/ に PasswordController.java を新規作成します。
最初は「APIが動く」ことだけを確認するために、最小限のエンドポイントを作ります。
package com.example.passwordchecker;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class PasswordController {
@GetMapping("/hello")
public String hello() {
return "Hello from Password Checker!";
}
}
PasswordCheckerApplication.java の緑の三角▶をクリックして起動します。コンソールに Tomcat started on port 8080 が表示されたらブラウザで http://localhost:8080/api/hello を開きます。
Hello from Password Checker! が表示されれば成功です。
5. Javaの「変数と型」を理解する
パスワードチェックのロジックを実装する前に、使う変数の型を確認します。
プログラムでデータを記憶する場所を変数と呼びます。Javaでは変数を作るとき**「何を入れるか(型)」を先に宣言する**必要があります。
String password = "hello"; // 文字列を入れる箱
int score = 0; // 整数を入れる箱
boolean isValid = true; // 真偽値を入れる箱
なぜ型を宣言するの? JavaScriptでは
let x = 10;と書けば自動的に「数値だな」と判断されます。しかしJavaは静的型付け言語で「この変数には必ず整数しか入らない」とコンパイル時に決定します。型の間違いをプログラムを実行する前に検出できます。
今回のチェッカーで使う主な型:
| 型名 | 意味 | 使い場面 |
|---|---|---|
String | 文字列 | パスワードの文字列 |
int | 整数 | 達成条件のスコア |
List<String> | 文字列のリスト | アドバイスの一覧 |
6. パスワード判定のロジックを実装する(要件2〜5)
正規表現で文字パターンをチェックする理由
「大文字が含まれるか」「数字が含まれるか」を調べるには、文字列の「パターン」を検索する必要があります。そのために正規表現を使います。
正規表現(Regular Expression)とは? 「大文字が1つ以上ある」「数字が含まれる」などの文字パターンを短い記号で表現するミニ言語です。
Stringクラスのmatches()メソッドで使えます。
".*[A-Z].*" の意味:
| 部分 | 意味 |
|---|---|
.* | 任意の文字が0文字以上(何でもOK) |
[A-Z] | AからZの大文字が1文字 |
.*[A-Z].* | 前後に何があってもよく、どこかに大文字が1文字以上ある |
"Hello".matches(".*[A-Z].*") // → true(Hが大文字)
"hello".matches(".*[A-Z].*") // → false(大文字なし)
"abc123".matches(".*[0-9].*") // → true(数字あり)
スコア計算とアドバイスのメソッドを実装する
各条件をチェックして1つずつスコアに加算します。未達成の条件はアドバイスとしてリストに追加します。
// 要件2〜4:各条件を満たすごとに+1してスコアを返す
private int calculateScore(String password) {
int score = 0;
if (password.length() >= 8) score++;
if (password.length() >= 12) score++;
if (password.matches(".*[A-Z].*")) score++;
if (password.matches(".*[a-z].*")) score++;
if (password.matches(".*[0-9].*")) score++;
if (password.matches(".*[!@#$%^&*()_+\\-=].*")) score++;
return score;
}
// 要件5:未達成の条件をアドバイスのリストで返す
private List<String> getAdvice(String password) {
List<String> advice = new ArrayList<>();
if (password.length() < 8)
advice.add("8文字以上にしてください(現在: " + password.length() + "文字)");
if (!password.matches(".*[A-Z].*")) advice.add("大文字(A-Z)を含めてください");
if (!password.matches(".*[a-z].*")) advice.add("小文字(a-z)を含めてください");
if (!password.matches(".*[0-9].*")) advice.add("数字(0-9)を含めてください");
if (!password.matches(".*[!@#$%^&*()_+\\-=].*")) advice.add("記号(!@#$%^&*)を含めてください");
return advice;
}
// 要件4:スコアを強度ラベルと★に変換する
private String getStrength(int score) {
if (score <= 2) return "弱い";
if (score <= 3) return "普通";
if (score <= 4) return "強い";
return "非常に強い";
}
private String getStars(int score) {
String[] stars = {"★☆☆☆☆","★☆☆☆☆","★★☆☆☆","★★★☆☆","★★★★☆","★★★★★","★★★★★"};
return stars[Math.min(score, stars.length - 1)];
}
7. DTOクラスを作る(要件1)
APIのリクエスト(入力)とレスポンス(出力)の形を定義するクラスが必要です。
DTOとは? 「このAPIにはこの形でJSONを送ってください」「このAPIはこの形のJSONを返します」というデータの形を定義するクラスです。
recordを使うと1行で作れます。
同じパッケージ内に2つのファイルを作成します。
PasswordRequest.java(リクエストの形 {"password": "hello"}):
package com.example.passwordchecker;
import jakarta.validation.constraints.NotBlank;
public record PasswordRequest(
@NotBlank(message = "パスワードは必須です")
String password
) {}
PasswordResponse.java(レスポンスの形):
package com.example.passwordchecker;
import java.util.List;
public record PasswordResponse(
int score,
String strength,
String stars,
List<String> advice
) {}
8. Controllerに組み込む(完成版)
要件1〜5をすべて組み合わせた PasswordController.java の完成版です。
package com.example.passwordchecker;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/api")
public class PasswordController {
// 要件1:POST /api/check-password でパスワードを受け取り、判定結果を返す
@PostMapping("/check-password")
public PasswordResponse checkPassword(@Valid @RequestBody PasswordRequest request) {
String password = request.password();
int score = calculateScore(password);
List<String> advice = getAdvice(password);
return new PasswordResponse(score, getStrength(score), getStars(score), advice);
}
// 要件2〜4:スコア計算
private int calculateScore(String password) {
int score = 0;
if (password.length() >= 8) score++;
if (password.length() >= 12) score++;
if (password.matches(".*[A-Z].*")) score++;
if (password.matches(".*[a-z].*")) score++;
if (password.matches(".*[0-9].*")) score++;
if (password.matches(".*[!@#$%^&*()_+\\-=].*")) score++;
return score;
}
// 要件5:アドバイス生成
private List<String> getAdvice(String password) {
List<String> advice = new ArrayList<>();
if (password.length() < 8)
advice.add("8文字以上にしてください(現在: " + password.length() + "文字)");
if (!password.matches(".*[A-Z].*")) advice.add("大文字(A-Z)を含めてください");
if (!password.matches(".*[a-z].*")) advice.add("小文字(a-z)を含めてください");
if (!password.matches(".*[0-9].*")) advice.add("数字(0-9)を含めてください");
if (!password.matches(".*[!@#$%^&*()_+\\-=].*")) advice.add("記号(!@#$%^&*)を含めてください");
return advice;
}
private String getStrength(int score) {
if (score <= 2) return "弱い";
if (score <= 3) return "普通";
if (score <= 4) return "強い";
return "非常に強い";
}
private String getStars(int score) {
String[] stars = {"★☆☆☆☆","★☆☆☆☆","★★☆☆☆","★★★☆☆","★★★★☆","★★★★★","★★★★★"};
return stars[Math.min(score, stars.length - 1)];
}
}
curlでテストします:
# 弱いパスワード
curl -X POST http://localhost:8080/api/check-password \
-H "Content-Type: application/json" \
-d '{"password": "hello"}'
# 強いパスワード
curl -X POST http://localhost:8080/api/check-password \
-H "Content-Type: application/json" \
-d '{"password": "Tr0ub4dor&3"}'
9. ブラウザ用HTMLフロントエンドを追加する(要件6)
src/main/resources/static/index.html を作成します。Spring Bootはこのフォルダのファイルを自動的にWebで公開します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>パスワード強度チェッカー</title>
<style>
body { font-family: sans-serif; max-width: 480px; margin: 60px auto; padding: 0 20px; }
h1 { font-size: 1.4rem; color: #1e293b; }
input {
width: 100%; padding: 10px; font-size: 1rem;
border: 1px solid #cbd5e1; border-radius: 6px; box-sizing: border-box;
}
button {
margin-top: 10px; padding: 10px 24px;
background: #4f46e5; color: white; border: none;
border-radius: 6px; cursor: pointer; font-size: 1rem;
}
button:hover { background: #4338ca; }
#result { margin-top: 20px; padding: 16px; background: #f8fafc; border-radius: 8px; display: none; }
.stars { font-size: 1.6rem; }
.strength { font-weight: bold; margin-left: 8px; font-size: 1.1rem; }
.advice { margin-top: 12px; padding-left: 16px; color: #64748b; }
.safe { color: #16a34a; font-weight: bold; margin-top: 10px; }
</style>
</head>
<body>
<h1>パスワード強度チェッカー</h1>
<input type="password" id="password" placeholder="パスワードを入力してください">
<br>
<button onclick="check()">チェックする</button>
<div id="result">
<div>
<span class="stars" id="stars"></span>
<span class="strength" id="strength"></span>
</div>
<ul class="advice" id="advice"></ul>
<p class="safe" id="safe" style="display:none">このパスワードは非常に安全です!</p>
</div>
<script>
async function check() {
const password = document.getElementById('password').value;
if (!password) return;
const res = await fetch('/api/check-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password })
});
const data = await res.json();
document.getElementById('stars').textContent = data.stars;
document.getElementById('strength').textContent = data.strength;
const adviceEl = document.getElementById('advice');
const safeEl = document.getElementById('safe');
if (data.advice.length === 0) {
adviceEl.innerHTML = '';
safeEl.style.display = 'block';
} else {
adviceEl.innerHTML = data.advice.map(a => `<li>${a}</li>`).join('');
safeEl.style.display = 'none';
}
document.getElementById('result').style.display = 'block';
}
</script>
</body>
</html>
http://localhost:8080/ を開くとHTMLフォームが表示されます。
10. GitHubに公開する
git init
git add .
git commit -m "feat: パスワード強度チェッカーAPI(Spring Boot)"
git remote add origin https://github.com/YOUR_NAME/password-checker.git
git push -u origin main
README.md に以下を記載してポートフォリオとして仕上げます。
# パスワード強度チェッカー API
Spring Boot で作成したパスワード強度判定 REST API。
## 技術スタック
- Java 21 / Spring Boot 3.3
- Spring Web / Bean Validation
## エンドポイント
| メソッド | パス | 説明 |
|--------|------|------|
| POST | /api/check-password | パスワード強度を判定してJSONで返す |
| GET | / | ブラウザ用HTMLフォーム |
## 使い方
\`\`\`bash
./mvnw spring-boot:run
curl -X POST http://localhost:8080/api/check-password \
-H "Content-Type: application/json" \
-d '{"password": "Tr0ub4dor&3"}'
\`\`\`
まとめ:学んだこと
| 概念 | 内容 | どこで使ったか |
|---|---|---|
| 要件定義 | コード前に「何を作るか」を言葉で整理する | 設計フェーズ |
| Spring Boot | WebサービスのJavaフレームワーク | プロジェクト全体の土台 |
@RestController | JSONを返すControllerの宣言 | PasswordController |
@PostMapping | POSTリクエストのエンドポイント定義 | /api/check-password |
@RequestBody | JSON→Javaオブジェクトの変換 | リクエスト受け取り |
record | シンプルなDTOクラス | PasswordRequest / PasswordResponse |
| 型と変数 | int / String でデータを記憶する | score / password |
| 正規表現 | matches() で文字パターンを検索する | 大文字・数字・記号チェック |
if / else if | 条件によって処理を変える | スコア→★への変換 |
List<String> | 複数のアドバイスをまとめる | advice |
Javaの学習シリーズ: 環境構築 | 今ここ(課題1・パスワードチェッカー) | 課題2・データ分析API | 課題3・為替変換API | 課題4・Spring Boot Todo API | 課題5・家計簿API
関連記事
CHECK IT OUT
サブスク加入者限定の
コンテンツを配信中
- 毎月のAI・自動化トレンドレポート
- クリエイター向け業務効率化テンプレート
- ツール選定・SaaS比較まとめ(限定公開)
「これ自動化できないかな?」
そのアイデア、ITで実現できます。
業務の自動化・お客様向けツール開発・AI活用まで、クリエイター・個人事業主専門のITコンサルタントが対応します。まずは気軽にご相談ください。