# 画面設計書 71-権限エラー画面

## 概要

本ドキュメントは、RuoYi後台管理システムの「権限エラー画面（unauth.html）」の設計書です。ユーザーが権限不足のリソースにアクセスしようとした場合に表示されるエラー画面について、画面構成、遷移フロー、技術的な実装詳細を記述しています。

### 本画面の処理概要

この画面は、ユーザーが自身の権限では許可されていない操作やリソースにアクセスしようとした際に表示される403エラー（Forbidden）画面です。Apache Shiroフレームワークによる権限認可チェックで拒否された場合に、システムが自動的にこの画面へリダイレクトします。

**業務上の目的・背景**：企業の後台管理システムでは、役職やロールに応じたアクセス制御が不可欠です。すべてのユーザーが全機能にアクセスできると、情報漏洩や不正操作のリスクが高まります。本画面は、権限のないユーザーに対して適切なエラーメッセージを表示し、不正アクセスを防止するとともに、ユーザーに対して権限が不足していることを明確に伝える役割を担っています。また、システム管理者への問い合わせを促進することで、必要に応じた権限付与プロセスを開始できるようにしています。

**画面へのアクセス方法**：この画面は直接アクセスする画面ではなく、以下の状況で自動的に表示されます。
1. ログイン済みユーザーが、割り当てられたロールに含まれない機能にアクセスしようとした場合
2. メニューから権限のない機能を選択しようとした場合（通常、権限のないメニューは非表示）
3. URLを直接入力して権限のないリソースにアクセスしようとした場合
4. API呼び出しで権限チェックに失敗した場合（非Ajax時）

**主要な操作・処理内容**：
1. 403エラーコードの表示により、権限不足であることを視覚的に伝達
2. 「您没有访问权限！」（アクセス権限がありません）というメッセージ表示
3. 「返回主页」（トップページへ戻る）ボタンによるメイン画面への遷移
4. 親フレーム全体をトップページに遷移させる処理（iframe構造を考慮）

**画面遷移**：
- 遷移元：システム内のすべての画面（権限チェックに失敗した場合）
- 遷移先：システム首頁（メイン画面）への「返回主页」ボタン押下時

**権限による表示制御**：本画面自体は権限に関係なく表示される静的なエラー画面です。ただし、本画面が表示される契機となる権限チェックは、コントローラメソッドに付与された`@RequiresPermissions`や`@RequiresRoles`アノテーションにより実施されます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | ログイン | 遷移先機能 | 権限不足時のエラー表示と再認証誘導 |

## 画面種別

エラー表示

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| URL | `/unauth` |
| HTTPメソッド | GET |
| コントローラ | `SysLoginController` |
| メソッド | `unauth()` |
| テンプレートパス | `error/unauth` |

### ルーティング設定

application.ymlにて以下のように設定されています。

```yaml
shiro:
  user:
    unauthorizedUrl: /unauth
```

Shiro権限チェックで認可エラーが発生した場合、`GlobalExceptionHandler`の`handleAuthorizationException`メソッドが`AuthorizationException`をキャッチし、非Ajaxリクエストの場合は`error/unauth`テンプレートへリダイレクトします。

## 入出力項目

本画面は入力項目を持たない読み取り専用のエラー表示画面です。

| 項目名 | 項目ID | 必須 | 型 | 入出力 | 説明 |
|--------|-------|------|-----|-------|------|
| なし | - | - | - | - | 入力項目なし |

## 表示項目

| 項目名 | 表示内容 | 説明 |
|--------|---------|------|
| エラーコード | 403 | HTTPステータスコード |
| タイトル | 您没有访问权限！ | アクセス権限がない旨のメッセージ |
| エラー説明文 | 对不起，您没有访问权限，请不要进行非法操作！您可以返回主页面 | ユーザーへの説明と案内 |
| 戻るボタン | 返回主页 | メイン画面への遷移ボタン |

## イベント仕様

### 1-返回主页ボタン押下

**トリガー**: 「返回主页」ボタンをクリック

**処理フロー**:
1. JavaScriptの`index()`関数が呼び出される
2. コンテキストパス（`ctx`）にThymeleafの`[[@{/}]]`から取得した値が設定される
3. `window.top.location`を使用して、親フレーム（iframe使用時の最上位ウィンドウ）全体のURLを変更
4. システム首頁（`/index`）へ遷移

**コード例**:
```javascript
var ctx = [[@{/}]];  // Thymeleafでコンテキストパスを取得
function index() {
    window.top.location = ctx + "index";  // 最上位フレームでトップページへ遷移
}
```

**遷移先**: システム首頁（`/index`）

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | なし | なし | データベースアクセスなし |
| 返回主页押下 | なし | なし | データベースアクセスなし |

本画面は静的なエラー表示のみのため、データベースへのアクセスは発生しません。

### テーブル別更新項目詳細

該当なし

## メッセージ仕様

| メッセージID | メッセージ種別 | メッセージ内容 | 表示条件 |
|-------------|--------------|--------------|---------|
| - | エラー | 403 | 常時表示 |
| - | エラー | 您没有访问权限！（アクセス権限がありません） | 常時表示 |
| - | 説明 | 对不起，您没有访问权限，请不要进行非法操作！您可以返回主页面（申し訳ございません。アクセス権限がありません。不正な操作を行わないでください。メインページに戻ることができます） | 常時表示 |

## 例外処理

| 例外種別 | 発生条件 | 対応処理 |
|---------|---------|---------|
| AuthorizationException | ユーザーが権限のないリソースにアクセス | GlobalExceptionHandlerでキャッチし、本画面（error/unauth）へ遷移 |

### 例外発生時のフロー

1. コントローラメソッドに`@RequiresPermissions`や`@RequiresRoles`アノテーションが付与されている
2. Shiroが現在のユーザーの権限情報をチェック
3. 権限が不足している場合、`AuthorizationException`をスロー
4. `GlobalExceptionHandler.handleAuthorizationException()`がキャッチ
5. リクエストの種類を判定
   - Ajaxリクエスト: JSONでエラーメッセージを返却
   - 通常リクエスト: `error/unauth`テンプレートへ遷移

## 備考

### 関連するエラー画面

| エラーコード | 画面名 | テンプレート | 説明 |
|-------------|--------|-------------|------|
| 404 | 404エラー画面 | error/404.html | ページが見つからない場合 |
| 500 | 500エラー画面 | error/500.html | サーバー内部エラー |
| - | サービスエラー画面 | error/service.html | サービス利用不可 |
| 403 | 権限エラー画面 | error/unauth.html | 権限不足（本画面） |

### 権限チェックの仕組み

Apache Shiroフレームワークを使用した権限チェックは以下のように実装されています。

1. **アノテーションによる権限定義**: コントローラメソッドに`@RequiresPermissions("system:user:list")`のようなアノテーションを付与
2. **UserRealmでの権限取得**: `doGetAuthorizationInfo()`メソッドでログインユーザーのロール・権限情報を取得
3. **権限チェック実行**: Shiroが自動的にメソッド実行前に権限をチェック
4. **例外ハンドリング**: 権限不足時は`AuthorizationException`がスローされ、本画面へ遷移

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

- 本画面では具体的にどの権限が不足しているかは表示されません（セキュリティ上の理由）
- 不正アクセスの試行はログに記録されます（`GlobalExceptionHandler`内で`log.error()`）
- 本画面からログイン画面への直接遷移は提供されておらず、トップページ経由となります

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

権限エラー画面では複雑なデータ構造は使用されませんが、Shiroの権限モデルを理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AuthorizationException | Apache Shiro ライブラリ | 権限チェック失敗時にスローされる例外クラス |

**読解のコツ**: Apache Shiroの例外階層を理解すると、どのような状況でこの画面が表示されるか明確になります。

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

権限エラー発生時の処理起点を特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | GlobalExceptionHandler.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java` | 権限例外のハンドリング処理 |
| 2-2 | SysLoginController.java | `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java` | `/unauth`エンドポイントの定義 |

**主要処理フロー**:
1. **GlobalExceptionHandler.java 36-48行目**: `@ExceptionHandler(AuthorizationException.class)`で権限例外をキャッチ
2. **GlobalExceptionHandler.java 41-48行目**: Ajaxリクエストかどうかで処理を分岐
3. **SysLoginController.java 77-81行目**: `/unauth`へのGETリクエストで`error/unauth`テンプレートを返却

#### Step 3: Shiro設定を理解する

Shiroフレームワークの設定を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ShiroConfig.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java` | Shiroフィルターチェーンと認可失敗時URL設定 |
| 3-2 | application.yml | `ruoyi-admin/src/main/resources/application.yml` | `shiro.user.unauthorizedUrl`の設定値 |

**主要処理フロー**:
- **ShiroConfig.java 127-128行目**: `unauthorizedUrl`プロパティの読み込み
- **ShiroConfig.java 301-302行目**: `shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl)`で認可失敗時の遷移先を設定
- **application.yml 97行目**: `unauthorizedUrl: /unauth`

#### Step 4: 権限認可処理を理解する

実際の権限チェック処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | UserRealm.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java` | ユーザーの権限情報取得処理 |

**主要処理フロー**:
- **UserRealm.java 56-81行目**: `doGetAuthorizationInfo()`メソッドでユーザーのロールと権限（パーミッション）を取得
- **UserRealm.java 66-69行目**: 管理者ユーザーの場合は全権限（`*:*:*`）を付与
- **UserRealm.java 73-78行目**: 一般ユーザーの場合はDBから権限を取得

#### Step 5: テンプレートを理解する

画面のHTMLテンプレートを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | unauth.html | `ruoyi-admin/src/main/resources/templates/error/unauth.html` | 403エラー画面のHTML構造 |

**主要処理フロー**:
- **unauth.html 13-14行目**: エラーコード403とメッセージの表示
- **unauth.html 18行目**: 「返回主页」ボタンのHTML
- **unauth.html 21-25行目**: トップページへ遷移するJavaScript関数

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

```
[権限チェック対象のコントローラ]
    │ @RequiresPermissions アノテーション
    ▼
[Apache Shiro フレームワーク]
    │ 権限チェック実行
    │ 権限不足時 AuthorizationException スロー
    ▼
[GlobalExceptionHandler.handleAuthorizationException()]
    │ 例外をキャッチ
    │ リクエストタイプ判定
    │
    ├─ [Ajax リクエスト]
    │      └─ AjaxResult.error() を返却
    │
    └─ [通常リクエスト]
           └─ ModelAndView("error/unauth") を返却
                  │
                  ▼
           [unauth.html テンプレート]
                  │ 「返回主页」ボタン押下
                  ▼
           [index() JavaScript関数]
                  │ window.top.location 変更
                  ▼
           [/index (システム首頁)]
```

### データフロー図

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

権限不足の         GlobalExceptionHandler          error/unauth.html
リソースへの   ───▶  AuthorizationException    ───▶   テンプレート
アクセス             キャッチ＆遷移                    レンダリング
                                                          │
                                                          ▼
「返回主页」       index() JS関数                  /index
ボタン押下    ───▶  window.top.location      ───▶   システム首頁
                   変更                             への遷移
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| unauth.html | `ruoyi-admin/src/main/resources/templates/error/unauth.html` | テンプレート | 403エラー画面のHTML |
| SysLoginController.java | `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java` | ソース | `/unauth`エンドポイント定義 |
| GlobalExceptionHandler.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java` | ソース | 権限例外ハンドリング |
| ShiroConfig.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java` | ソース | Shiroフレームワーク設定 |
| UserRealm.java | `ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java` | ソース | 権限認可処理 |
| application.yml | `ruoyi-admin/src/main/resources/application.yml` | 設定 | Shiro関連設定値 |
| bootstrap.min.css | `ruoyi-admin/src/main/resources/static/css/bootstrap.min.css` | スタイル | Bootstrapフレームワーク |
| animate.min.css | `ruoyi-admin/src/main/resources/static/css/animate.min.css` | スタイル | アニメーション効果 |
| style.min.css | `ruoyi-admin/src/main/resources/static/css/style.min.css` | スタイル | カスタムスタイル |
