web-dev-qa-db-ja.com

Hibernateフィルターまたは他の方法を使用してSpringData JPAに行レベルのセキュリティを実装する方法は?

情報ソフトウェアにおける非常に重要な問題の1つは、さまざまな職務とアクセスレベルを持つさまざまな役割を持つユーザーの存在です。たとえば、次のような構造(階層)を持つ組織について考えてみます。

[Organization Role ]     [Organization ID]
 CEO                        org01
   Financial Assistant      org0101
           personnel 1

   Software Assistant       org0102
           personnel 2

   Commercial Assistant     org0103
           personnel 3

この組織に、人員の情報を管理するシステムがあると想像してみてください。このシステムで担当者の情報を表示するルールは、各ユーザーがアクセスできる組織の担当者の情報を表示できることです。たとえば、「user1」は「FinancialAssistant」レベルと「CommercialAssistant」レベルにアクセスできるため、「personnel1」と「personnel3」の情報しか表示できません。同様に、「user2」は「Commercial Assistant」レベルにしかアクセスできないため、「personnel3」の情報しか見ることができません。したがって、このシステムの各ユーザーには特定のアクセスレベルがあります。ここで、このシステムでは、各ユーザーには、ログイン後にアクセスできる個人情報のみが表示されることを考慮してください。このシステムのテーブル構造は次のようになります。

[Organization]
id
code
name

[Employee]
id
first_name
last_name
organization_id

[User]
id
user_name
password

[UserOrganization]
user_id
organization_id

以下のクエリは、各ユーザーの適切な人事情報の結果を取得するのに十分です。

select *

from employee e 

where e.organization_id in

(select uo.organization_id

 from user_organization uo

 where uo.user_id=:authenticatedUserId)

ご覧のとおり、以下の条件は、適切なデータを表示するためのアクセスロジックを定義します。

e.organization_id in

(select uo.organization_id

 from user_organization uo

 where uo.user_id=:authenticatedUserId)

この種のアクセスレベルは、「行レベルセキュリティ」(RLS)とも呼ばれます。一方、対応するリポジトリクラスには、おそらくデータの読み取りを担当するいくつかのメソッドがあり、それらはすべて適切なアクセスレベル条件を満たす必要があります。この場合、アクセスレベル条件はいくつかの場所(メソッド)で繰り返されます。 「休止状態フィルター」を使用することが、この問題の適切な解決策になると思われます。必要なのは、認証されたユーザーのIDを取得し、すべての読み取りメソッドの前に「enablefilter」コマンドを実行するフィルターだけです。

@Filters( {
  @Filter(name=“EmployeeAuthorize", condition="(organization_id in (select uo.organization_id from user_organization uo where uo.user_id=:authenticatedUserId) )  ")
} )

さて、問題は、提案された解決策は正しいのかということです。はいの場合、この方法を春のデータでどのように利用できますか? PS:データベースに依存したくないので、データベース側での実装は候補ソリューションにはなり得ません。そのため、アプリケーション側(レベル)で実装する必要があります。

10

ALi、それは興味深いシナリオです。

2つの質問ここで答える必要があります。

最初の質問-データを公開するとき、システムはフィルタリングを実行するだけですか、それともそれを超えますか?たとえば、users/{id}のような操作を公開する場合は、承認を確認し、ユーザーがその操作にアクセスできることを確認する必要があります。 / usersのような操作を単純に公開する場合-現在のユーザーが許可されているユーザーを公開するだけなので、必要なのはフィルタリングだけです。見る。その違いにより、多くの実装が決まります。


2番目の質問は-どのくらいの手作業で大丈夫ですか?

一方では、フレームワークが必要とするものにデータを適合させることができます-そして、組み込み機能(セキュリティ式、ACL)に可能な限り依存しようとすることができます。または、一方で、コードをデータの構造に適合させて、より手動で行うこともできます。

これらは、私が最初に焦点を当てる2つの要素です。何よりもまず、これら4つの決定に基づいて実装が完全に異なるように見えるためです。


最後に、「ACLをスケーリングできるか」という質問に答えるために、2つの簡単なメモを示します。 1つ-テストする必要があります。はい、ACLは拡張できますが、10Kまたは100Kに拡張できるかどうかは、テストなしで具体的に答えることができる質問ではありません。

次に、テストを行うときは、現実的なシナリオを検討してください。ソリューションの限界を理解することは確かに重要です。しかし、それを超えて、システムに100万のエンティティがあると思うなら-素晴らしいです。しかし、そうでない場合は、それを目標にしないでください。

お役に立てば幸いです。

5
Eugen

Springでは、次のものを使用できます。

1) SpEL EvaluationContext extension を使用して、@ QueryアノテーションのSpEL式でセキュリティプロパティと式を使用できるようにすることができます。これにより、現在のユーザーに関連するビジネスオブジェクトのみを取得できます。

interface SecureBusinessObjectRepository extends Repository<BusinessObject, Long> {

    @Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
    List<BusinessObject> findBusinessObjectsForCurrentUser();
}

2)Webセキュリティ式で Beansを参照 および パス変数 を使用できます。これにより、現在のユーザーに許可されているオブジェクトのみにアクセスを許可できます。

@Service
public class UserService {
    public boolean checkAccess(Authentication authentication, int id) {
        // ...
    }
}

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/businessObjects/{id}/**").access("@userService.checkAccess(authentication, #id)")
            // ...
    }
}

詳細については、 私のデモプロジェクト を確認してください。この例では、ユーザーは、これらのユーザーに関連するカテゴリに属している場合、ルームにアクセスできます。管理者はすべての部屋にアクセスできます。

3
Cepr0