LastaFluteプロジェクトでアクセス権限管理をしてみます。
会員マスタに権限管理用のカラムを追加し、区分値とアノテーションを利用して判定する方法でやります。
開発環境:
LastaFlute(1.0.0)
MySQL(5.7)
下準備
会員権限マスタ
会員権限区分値用のマスタを追加します。
マスタを用意せず、手書きで区分値を追加してもいいかと思います。
今回は、以下の3つの権限を作成しました。
会員権限コード(PK) MEMBER_AUTHORITY_CODE |
会員権限名称 MEMBER_AUTHORITY_NAME |
説明 DESCRIPTION |
---|---|---|
CMN | Common | 一般 |
MNG | Manager | 管理者 |
SYS | System manager | システム管理者 |
会員マスタ
会員マスタに会員権限コードを追加し、会員権限マスタを外部キー定義します。
会員ID(PK) MEMBER_ID |
会員名称 MEMBER_NAME |
会員アカウント MEMBER_ACCOUNT |
会員権限コード(FK) MEMBER_AUTHORITY_CODE |
---|---|---|---|
1 | 一般会員 | M1 | CMN |
2 | システム管理者 | M2 | SYS |
区分値(CDef)
作成した会員権限マスタを区分値で使えるようにします。
# classificationDefinitionMap ; MemberAuthority = list:{ ; map:{ ; topComment=会員権限; codeType=String } ; map:{ ; table=MEMBER_AUTHORITY ; code=MEMBER_AUTHORITY_CODE; name=MEMBER_AUTHORITY_NAME ; comment=DESCRIPTION } }
アノテーション
アクセス権限管理用のアノテーションを作成します。
※今回は、Actionクラスにアノテーションを付ける想定です。
// java /** アクセス権限 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Accessible { public MemberAuthority[] auth(); }
実装
Mainという名前のアプリにアクセス権限を実装していきます。
UserBean
MainUserBeanに会員権限を追加します。
// MainUserBean // メンバに会員権限を追加する // ※コンストラクタでの設定と、getterも追加 protected final MemberAuthority memberAuthority;
Actionクラスにアノテーションを追加
今回は、アノテーションがあるActionのみアクセス権限管理の対象にするようにします。
画面A:アクセス権限管理対象外
画面B:管理者とシステム管理者のみアクセス可
/** 画面AのActionClass */ // アクセス権限管理対象外なので、アノテーションなし public class AuthorityAAction extends MainBaseAction { @Execute public HtmlResponse index() { return asHtml(path_Authority_AuthorityAHtml); } }
/** 画面BのActionClass */ // ManagerとSystemManagerのみアクセス可にするアノテーションを追加 @Accessible(auth = { MemberAuthority.Manager, MemberAuthority.SystemManager }) public class AuthorityBAction extends MainBaseAction { @Execute public HtmlResponse index() { return asHtml(path_Authority_AuthorityBHtml); } }
アクセス権限判定処理
Actionに設定されたアノテーションを読み取り、アクセス権限を判定する処理を作成します。
MainLoginAssist クラスで、 checkPermission メソッドをオーバーライドします。
// java // MainLoginAssist @Override protected void checkPermission(LoginHandlingResource resource) throws LoginRequiredException { // Actionクラスのアノテーション取得 Accessible accessible = resource.getActionClass().getAnnotation(Accessible.class); if (accessible == null) { // Accessibleアノテーションがなければ、アクセス権限チェック対象外 return; } // ログイン会員の会員権限を取得 MainUserBean ub = getSavedUserBean().get(); MemberAuthority memberAuth = ub.getMemberAuthority(); for (MemberAuthority auth : accessible.auth()) { if (auth.equals(memberAuth)) { // ログイン会員の会員権限がアノテーションに定義してあればアクセス可 return; } } // 上記処理で会員権限が該当しなかった場合、アクセス不可 // LoginRequiredExceptionをスローします。(ログ出力内容の粒度はお好みで。) throw new LoginRequiredException( String.format("アクセス不可[%s, 権限:%s][会員ID:%d, 氏名:%s, 権限:%s]", resource.getActionClass().getSimpleName(), Arrays.asList(accessible.auth()), ub.getUserId(), ub.getMemberName(), ub.getMemberAuthority())); }
動作確認
一般会員でログインし、画面Aを表示
一般会員でログインし、画面Bを表示。
※以下のようにログが出力されます。
※抜粋 org.lastaflute.web.login.exception.LoginRequiredException: アクセス不可[AuthorityBAction, 権限:[MNG, SYS]][会員ID:1, 氏名:一般会員, 権限:CMN]
システム管理者でログインし、画面Aを表示
システム管理者でログインし、画面Bを表示
最後に
今回はActionクラス単位で判定しましたが、作り方次第でメソッド単位での判定も可能です。
また、アノテーションを利用しなくてもクラス名とユーザーの権限で判定してしまう事も可能です。(良いか悪いかは…どうなんでしょう)
他にもシステム管理者は無条件でアクセス可にしたり。などなど。
要するに、プロジェクトに合わせた色々な実装方法があると思うので、この記事がひとつの参考になれば嬉しいです。