# 機能設計書 19-ログイン

## 概要

本ドキュメントは、RuoYiシステムにおけるログイン機能の詳細設計を記述する。ログイン機能は、ユーザー認証を行い、システムへのアクセスを制御する基本的なセキュリティ機能である。

### 本機能の処理概要

ログイン機能は、Apache Shiroフレームワークを使用してユーザー認証を行い、認証成功後にセッションを確立する機能を提供する。キャプチャ検証、パスワード検証、アカウント状態チェック、ログイン履歴記録などの包括的な認証処理を行う。

**業務上の目的・背景**：システムセキュリティの最前線として、正当なユーザーのみがシステムにアクセスできるようにする。不正アクセスの防止、ブルートフォース攻撃への対策、ユーザー行動の追跡（監査）などを通じて、システムの安全性を確保する。

**機能の利用シーン**：
- ユーザーがシステムにアクセスする際の認証
- セッションタイムアウト後の再認証
- パスワード変更後の再ログイン
- 「記憶する」機能によるログイン状態の維持

**主要な処理内容**：
1. ログイン画面の表示
2. キャプチャ（画像認証コード）の検証
3. ユーザー名・パスワードの検証
4. IPブラックリストチェック
5. アカウント状態（削除・無効化）の確認
6. パスワード試行回数制限
7. ログイン成功/失敗の記録
8. セッションの確立
9. 権限情報の設定
10. 「記憶する」機能の処理

**関連システム・外部連携**：Apache Shiro、EhCacheと連携。

**権限による制御**：
- ログイン機能自体は認証前のため権限制御なし
- ログイン後、Shiroにより権限が設定される

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ログイン画面 | 主画面 | ユーザー名、パスワード入力によるログイン認証処理 |
| 3 | 画面ロック | 関連画面 | パスワード入力による画面ロック解除認証処理 |
| 4 | システム首頁 | 遷移先 | ログイン成功後の遷移先 |
| 71 | 権限エラー画面 | エラー画面 | 権限不足時のエラー表示と再認証誘導 |

## 機能種別

認証処理 / セッション管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| username | String | Yes | ユーザー名 | 2〜20文字 |
| password | String | Yes | パスワード | 5〜20文字 |
| rememberMe | Boolean | No | ログイン状態を記憶 | true/false |
| captcha | String | Yes（設定時） | 画像認証コード | 正しいコード |

### 入力データソース

- 画面入力
- Cookieからの「記憶する」情報

## 出力仕様

### 出力データ

#### ログイン成功時

| 項目名 | 型 | 説明 |
|--------|-----|------|
| - | - | リダイレクト（indexページ） |

#### ログイン失敗時（Ajax）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | String | エラーコード |
| msg | String | エラーメッセージ |

### 出力先

- 画面遷移（成功時）
- AjaxResult（失敗時）

## 処理フロー

### 処理シーケンス

```
1. ログイン画面表示
   └─ GET /login リクエスト
   └─ Ajaxリクエストの場合、JSONエラーレスポンス返却
   └─ 「記憶する」設定、ユーザー登録許可設定を取得
   └─ ログイン画面を表示

2. ログイン処理
   └─ POST /login リクエスト
   └─ UsernamePasswordToken生成
   └─ Subject.login()呼び出し
      └─ UserRealm.doGetAuthenticationInfo()
         └─ SysLoginService.login()
            └─ キャプチャ検証
            └─ 入力値長さ検証
            └─ IPブラックリストチェック
            └─ ユーザー情報取得
            └─ アカウント状態チェック（削除・無効化）
            └─ パスワード検証（試行回数制限含む）
            └─ ログイン成功記録
            └─ 権限設定
            └─ ログイン情報更新
   └─ 成功レスポンス返却

3. 認証エラー時
   └─ AuthenticationException捕捉
   └─ エラーメッセージを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{GET/POST}
    B -->|GET| C{Ajaxリクエスト?}
    C -->|Yes| D[JSONエラー返却]
    C -->|No| E[ログイン画面表示]

    B -->|POST| F[Token生成]
    F --> G[Subject.login]
    G --> H{キャプチャ検証}
    H -->|NG| I[エラー: 验证码错误]
    H -->|OK| J{入力長検証}
    J -->|NG| K[エラー: 長さ不正]
    J -->|OK| L{IPブラックリスト}
    L -->|該当| M[エラー: login.blocked]
    L -->|OK| N{ユーザー存在?}
    N -->|No| O[エラー: 用户不存在]
    N -->|Yes| P{削除済み?}
    P -->|Yes| Q[エラー: 已删除]
    P -->|No| R{無効化?}
    R -->|Yes| S[エラー: 已禁用]
    R -->|No| T{試行回数超過?}
    T -->|Yes| U[エラー: 重试次数超限]
    T -->|No| V{パスワード一致?}
    V -->|No| W[エラー: 密码错误]
    V -->|Yes| X[ログイン成功記録]
    X --> Y[権限設定]
    Y --> Z[ログイン情報更新]
    Z --> AA[成功レスポンス]

    D --> AB[終了]
    E --> AB
    I --> AB
    K --> AB
    M --> AB
    O --> AB
    Q --> AB
    S --> AB
    U --> AB
    W --> AB
    AA --> AB
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-19-01 | キャプチャ必須 | 画像認証コードが有効な場合、検証必須 | キャプチャ有効設定時 |
| BR-19-02 | ユーザー名長さ | 2〜20文字 | ログイン時 |
| BR-19-03 | パスワード長さ | 5〜20文字 | ログイン時 |
| BR-19-04 | 試行回数制限 | 設定回数を超えるとアカウントロック | ログイン失敗時 |
| BR-19-05 | IPブラックリスト | 設定されたIPからのアクセスを拒否 | ログイン時 |
| BR-19-06 | アカウント状態 | 削除済み・無効化されたアカウントはログイン不可 | ログイン時 |
| BR-19-07 | ログイン記録 | 成功・失敗とも非同期でログ記録 | ログイン処理時 |
| BR-19-08 | 記憶する機能 | rememberMe=trueで次回自動ログイン | ログイン成功時 |

### 計算ロジック

#### パスワードハッシュ化

```
hashedPassword = MD5(loginName + password + salt)
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー取得 | sys_user | SELECT | ユーザー情報を取得 |
| ログイン記録 | sys_logininfor | INSERT | ログイン履歴を記録 |
| ログイン情報更新 | sys_user | UPDATE | 最終ログイン日時・IPを更新 |

### テーブル別操作詳細

#### sys_user

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | WHERE login_name = ? AND del_flag = '0' | ユーザー取得 |
| UPDATE | login_ip, login_date | 現在IP, 現在日時 | ログイン成功時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | CaptchaException | キャプチャ不一致 | 正しいコードを入力 |
| - | UserNotExistsException | ユーザー未存在/入力不正 | 正しい情報を入力 |
| - | UserPasswordNotMatchException | パスワード不一致 | 正しいパスワードを入力 |
| - | UserPasswordRetryLimitExceedException | 試行回数超過 | 管理者に連絡してロック解除 |
| - | UserDeleteException | アカウント削除済み | 管理者に連絡 |
| - | UserBlockedException | アカウント無効化 | 管理者に連絡 |
| - | BlackListException | IPブラックリスト該当 | 管理者に連絡 |

### リトライ仕様

- ログイン失敗回数はEhCacheで管理
- 設定回数（user.password.maxRetryCount）超過でロック
- ログイン成功またはロック解除でカウントリセット

## トランザクション仕様

- ログイン処理自体は読み取り主体のためトランザクション明示なし
- ログイン記録は非同期で実行（AsyncManager）

## パフォーマンス要件

- ログイン処理：2秒以内にレスポンス
- パスワードハッシュ計算：100ms以内

## セキュリティ考慮事項

- パスワードはMD5+ソルトでハッシュ化
- ブルートフォース攻撃対策（試行回数制限）
- キャプチャによるボット対策
- IPブラックリストによる不正アクセス防止
- セッション固定攻撃対策（Shiroによる）
- ログイン失敗時も具体的なエラー理由を限定的に返却

## 備考

- ShiroのUserRealmでSysLoginService.login()を呼び出す
- 非同期ログ記録はAsyncFactory.recordLogininfor()を使用
- 「記憶する」機能はShiroのRememberMe機能を使用
- shiro.rememberMe.enabled設定で機能の有効/無効を切替

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: エントリーポイントを理解する

コントローラーが処理の起点となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SysLoginController.java | `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java` | ログイン画面表示とAjaxログイン処理 |

**主要処理フロー**:
- **40-53行目**: login() - GET リクエスト、画面表示
- **55-75行目**: ajaxLogin() - POST リクエスト、認証処理
- **77-81行目**: unauth() - 権限エラー画面

#### Step 2: Shiro認証を理解する

Shiroの認証フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | UserRealm.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java` | Shiroのカスタムレルム。doGetAuthenticationInfo()で認証 |

#### Step 3: ログインサービスを理解する

認証ロジックの中核を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SysLoginService.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysLoginService.java` | 認証ロジック全体 |

**主要処理フロー**:
- **54-131行目**: login() - メイン認証処理
- **57-61行目**: キャプチャ検証
- **63-82行目**: 入力値長さ検証
- **84-90行目**: IPブラックリストチェック
- **93-111行目**: ユーザー存在・状態チェック
- **113-117行目**: 削除済み・無効化チェック
- **125行目**: パスワード検証
- **127-129行目**: ログイン成功処理

#### Step 4: パスワードサービスを理解する

パスワード検証と試行回数制限を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SysPasswordService.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysPasswordService.java` | パスワード検証、試行回数管理 |

**主要処理フロー**:
- **42-69行目**: validate() - パスワード検証と試行回数チェック
- **71-74行目**: matches() - パスワード一致判定
- **76-79行目**: clearLoginRecordCache() - ロック解除
- **81-84行目**: encryptPassword() - パスワードハッシュ化

### プログラム呼び出し階層図

```
SysLoginController
    │
    ├─ login() [GET]
    │      ├─ ServletUtils.isAjaxRequest()
    │      ├─ configService.getKey("sys.account.registerUser")
    │      └─ return "login"
    │
    └─ ajaxLogin() [POST]
           ├─ new UsernamePasswordToken()
           └─ SecurityUtils.getSubject().login()
                  │
                  └─ UserRealm.doGetAuthenticationInfo()
                         │
                         └─ SysLoginService.login()
                                ├─ キャプチャ検証
                                ├─ 入力値検証
                                ├─ IPブラックリストチェック
                                ├─ userService.selectUserByLoginName()
                                ├─ アカウント状態チェック
                                ├─ SysPasswordService.validate()
                                │      └─ loginRecordCache管理
                                │      └─ encryptPassword()
                                │      └─ matches()
                                ├─ AsyncFactory.recordLogininfor()
                                ├─ setRolePermission()
                                └─ recordLoginInfo()
```

### データフロー図

```
[入力]               [処理]                    [出力]

username     ───▶ Controller         ───▶ AjaxResult
password           ↓                        (success/error)
rememberMe         Subject.login()
                   ↓
                   UserRealm
                   ↓
                   SysLoginService.login()
                   ↓
              ┌────┴────────────────┐
              ↓                     ↓
         Validation            Authentication
         (captcha,             (user, password,
          length,              retry count,
          blacklist)           account status)
              ↓                     ↓
              └────────┬────────────┘
                       ↓
                 Success/Failure
                       ↓
              ┌────────┴────────┐
              ↓                 ↓
         recordLogininfor   セッション確立
         (非同期)           権限設定
                            ログイン情報更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SysLoginController.java | `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/` | コントローラー | HTTPリクエストのハンドリング |
| SysLoginService.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/` | サービス | 認証ロジック |
| SysPasswordService.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/` | サービス | パスワード検証・試行回数管理 |
| UserRealm.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/` | Shiro Realm | Shiro認証・認可 |
| ConfigService.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/` | サービス | システム設定取得 |
| AsyncFactory.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/` | ファクトリー | 非同期タスク生成 |
| ShiroConstants.java | `ruoyi-common/src/main/java/com/ruoyi/common/constant/` | 定数 | Shiro関連定数 |
| UserConstants.java | `ruoyi-common/src/main/java/com/ruoyi/common/constant/` | 定数 | ユーザー関連定数 |
