1. 序章

このチュートリアルでは、きめ細かいアクセス許可ベースのアクセス制御 Apache ShiroJavaセキュリティフレームワークで実装する方法を見ていきます。

2. 設定

Shiroの紹介と同じセットアップを使用します。つまり、shiro-coreモジュールのみを依存関係に追加します。

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.1</version>
</dependency>

さらに、テストの目的で、クラスパスのルートに次の shiro.ini ファイルを配置することにより、単純なINIレルムを使用します。

[users]
jane.admin = password, admin
john.editor = password2, editor
zoe.author = password3, author
 
[roles]
admin = *
editor = articles:*
author = articles:create, articles:edit

次に、上記のレルムでShiroを初期化します。

IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
SecurityManager securityManager = new DefaultSecurityManager(iniRealm);
SecurityUtils.setSecurityManager(securityManager);

3. 役割と権限

通常、認証と承認について話すときは、ユーザーとロールの概念に焦点を当てます。

特に、ロールは、アプリケーションまたはサービスのユーザーの分野横断的なクラスです。したがって、特定のロールを持つすべてのユーザーは、一部のリソースと操作にアクセスでき、他の部分へのアクセスが制限されている可能性があります。アプリケーションまたはサービス。

一連の役割は通常、事前に設計されており、新しいビジネス要件に対応するために変更されることはめったにありません。 ただし、役割は、たとえば管理者によって動的に定義することもできます。

Shiroでは、ユーザーが特定の役割を持っているかどうかをテストするいくつかの方法があります。 最も簡単な方法は、hasRoleメソッドを使用することです。

Subject subject = SecurityUtils.getSubject();
if (subject.hasRole("admin")) {       
    logger.info("Welcome Admin");              
}

3.1. 権限

ただし、ユーザーが特定の役割を持っているかどうかをテストして承認を確認すると、問題が発生します。 実際、ロールと権限の関係をハードコーディングしています。つまり、リソースへのアクセスを許可または取り消す場合は、ソースコードを変更する必要があります。 もちろん、これは再構築と再デプロイも意味します。

私たちはもっとうまくやることができます。 そのため、ここでは権限の概念を紹介します。 権限は、ソフトウェアが実行できることを表します承認または拒否できること、および誰がそれをすることができるかではありません。 たとえば、「現在のユーザーのプロファイルを編集する」、「ドキュメントを承認する」、「新しい記事を作成する」などです。

Shiroは、権限についてほとんど想定していません。 最も単純なケースでは、権限はプレーンな文字列です。

Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:create")) {
    //Create a new article
}

パーミッションの使用はShiroでは完全にオプションであることに注意してください。

3.2. ユーザーへのアクセス許可の関連付け

Shiroには、権限をロールまたは個々のユーザーに関連付ける柔軟なモデルがあります。 ただし、このチュートリアルで使用している単純なINIレルムを含む一般的なレルムは、権限をロールに関連付けるだけです。

したがって、プリンシパルによって識別されるユーザーには複数の役割があり、各役割には複数の権限があります。

たとえば、INIファイルでは、ユーザーzoe.authorauthorの役割を持っており、 articles:createが与えられていることがわかります。 ] articles:edit 権限:

[users]
zoe.author = password3, author
#Other users...

[roles]
author = articles:create, articles:edit
#Other roles...

同様に、他のレルムタイプ(組み込みのJDBCレルムなど)を構成して、アクセス許可をロールに関連付けることができます。

4. ワイルドカードのアクセス許可

Shiroでのアクセス許可のデフォルトの実装は、ワイルドカードアクセス許可です。さまざまなアクセス許可スキームの柔軟な表現です。

Shiroのワイルドカード権限を文字列で表します。 許可文字列は、次のように、コロンで区切られた1つ以上のコンポーネントで構成されます。

articles:edit:1

Shiroはルールを適用しないため、文字列の各部分の意味はアプリケーションによって異なります。 ただし、上記の例では、文字列を階層として非常に明確に解釈できます。

  1. 公開しているリソースのクラス(記事)
  2. そのようなリソースに対するアクション(編集)
  3. アクションを許可または拒否する特定のリソースのID

resource:action:idのこの3層構造は、多くの異なるシナリオを表現するのにシンプルかつ効果的であるため、Shiroアプリケーションで一般的なパターンです。

したがって、前の例に戻って、このスキームに従うことができます。

Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:edit:123")) {
    //Edit article with id 123
}

ワイルドカード権限文字列内のコンポーネントの数は、通常は3つのコンポーネントですが、3つである必要はないことに注意してください。

4.1. 権限の意味とインスタンスレベルの粒度

ワイルドカードのアクセス許可は、Shiroのアクセス許可の別の機能である含意と組み合わせると効果的です。

ロールをテストするときは、正確なメンバーシップをテストします。 サブジェクトが特定のロールを持っているか、持っていないかのどちらかです。 言い換えれば、シロは役割が平等であるかどうかをテストします。

一方、パーミッションをテストするときは、含意をテストします。 Subject のパーミッションは、テスト対象のパーミッションを意味しますか?

意味するところは、具体的には許可の実施によって異なります。 実際、ワイルドカードのアクセス許可の場合、名前が示すように、ワイルドコンポーネントの可能性がある、部分的な文字列の一致が意味します。

したがって、作成者ロールに次の権限を割り当てたとします。

[roles]
author = articles:*

次に、作成者の役割を持つすべてのユーザーに、記事に対するすべての可能な操作が許可されます。

Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:create")) {
    //Create a new article
}

つまり、文字列 articles:* は、最初のコンポーネントがarticles。であるワイルドカード権限と一致します。

このスキームを使用すると、非常に特定の権限(特定のIDを持つ特定のリソースに対する特定のアクション)または、記事の編集や記事に対する操作の実行などの幅広い権限の両方を割り当てることができます。

もちろん、パフォーマンス上の理由から、含意は単純な同等性の比較ではないため、常に最も具体的な権限に対してテストする必要があります。

if (subject.isPermitted("articles:edit:1")) { //Better than "articles:*"
    //Edit article
}

5. カスタム権限の実装

権限のカスタマイズについて簡単に触れてみましょう。 ワイルドカードのアクセス許可は広範囲のシナリオをカバーしますが、アプリケーション用にカスタムメイドのソリューションに置き換えることもできます。

パスのパーミッションがすべてのサブパスのパーミッションを暗示するように、パスのパーミッションをモデル化する必要があるとします。 実際には、タスクにワイルドカードのアクセス許可を適切に使用できますが、それは無視しましょう。

では、何が必要ですか?

  1. 権限の実装
  2. シロにそれを伝えるために

両方のポイントを達成する方法を見てみましょう。

5.1. パーミッション実装の作成

パーミッションの実装は、単一のメソッドを持つクラスです— を意味します:

public class PathPermission implements Permission {

    private final Path path;

    public PathPermission(Path path) {
        this.path = path;
    }

    @Override
    public boolean implies(Permission p) {
        if(p instanceof PathPermission) {
            return ((PathPermission) p).path.startsWith(path);
        }
        return false;
    }
}

このメソッドは、 thisが他のアクセス許可オブジェクトを意味する場合はtrueを返し、それ以外の場合はfalseを返します。

5.2. シロに私たちの実装について伝える

次に、 Permission 実装をShiroに統合するさまざまな方法がありますが、最も簡単な方法は、カスタムPermissionResolverをレルムに注入することです:

IniRealm realm = new IniRealm();
Ini ini = Ini.fromResourcePath(Main.class.getResource("/com/.../shiro.ini").getPath());
realm.setIni(ini);
realm.setPermissionResolver(new PathPermissionResolver());
realm.init();

SecurityManager securityManager = new DefaultSecurityManager(realm);

PermissionResolverが責任を負います 為に パーミッションの文字列表現を実際のパーミッションオブジェクトに変換します。

public class PathPermissionResolver implements PermissionResolver {
    @Override
    public Permission resolvePermission(String permissionString) {
        return new PathPermission(Paths.get(permissionString));
    }
}

以前のshiro.iniをパスベースの権限で変更する必要があります。

[roles]
admin = /
editor = /articles
author = /articles/drafts

次に、パスの権限を確認できます。

if(currentUser.isPermitted("/articles/drafts/new-article")) {
    log.info("You can access articles");
}

ここでは、プログラムで単純なレルムを構成していることに注意してください。 通常のアプリケーションでは、 shiro.ini ファイルまたはSpringなどの他の手段を使用して、Shiroとレルムを構成します。 実際のshiro.iniファイルには次のものが含まれている可能性があります。

[main]
permissionResolver = com.baeldung.shiro.permissions.custom.PathPermissionResolver
dataSource = org.apache.shiro.jndi.JndiObjectFactory
dataSource.resourceName = java://app/jdbc/myDataSource

jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $dataSource 
jdbcRealm.permissionResolver = $permissionResolver

6. 結論

この記事では、ApacheShiroがアクセス許可ベースのアクセス制御を実装する方法を確認しました。

いつものように、これらすべての例とコードスニペットの実装は、GitHub利用できます。