1前書き


アクセス制御リスト



ACL)

は、オブジェクトに割り当てられているアクセス許可の一覧です。

ACL

は、特定のオブジェクトに対するどの操作にどの操作を許可するかを指定します。


Springセキュリティ

アクセス制御リスト

は、

ドメインオブジェクトセキュリティをサポートする

Spring

コンポーネントです。簡単に言うと、Spring ACLは、通常の操作ごとではなく、単一のドメインオブジェクトに対する特定のユーザー/ロールのアクセス許可を定義するのに役立ちます。レベル。

たとえば、ロール

Admin

を持つユーザーは、

Central Notice Box内のすべてのメッセージを表示(

READ)

および編集(

WRITE)

することができますが、通常のユーザーはメッセージを見ることしかできず、メッセージに関連し、編集できません。一方、役割

Editor__を持つ他のユーザーは、特定のメッセージを表示および編集できます。

したがって、異なるユーザー/ロールには、特定のオブジェクトごとに異なる権限があります。この場合、

Spring ACL

はタスクを実行できます。

この記事では、

Spring ACL

を使用した基本的な権限チェックの設定方法を探ります。


2構成


2.1. ACLデータベース


Spring Security ACL

を使用するには、データベースに4つの必須テーブルを作成する必要があります。

最初のテーブルは

ACL

CLASS__で、ドメインオブジェクトのクラス名が格納されています。カラムは次のとおりです。


  • ID


  • CLASS:

    保護されたドメインオブジェクトのクラス名。次に例を示します。



_org.baeldung.acl.persistence.entity.NoticeMessage

_

第二に、システム内の原則や権限を普遍的に識別できるようにする

ACL

SID__テーブルが必要です。テーブルには以下が必要です。


  • ID


  • SID:

    これはユーザー名またはロール名です。

    SID

    は__Securityを表します

身元

**

PRINCIPAL:


0

または

1

、対応する

SID__が

主体(

mary、mike、jack …​

などのユーザー)または機関(

ROLE

ADMIN、ROLE

USER、ROLE

EDITOR …​__などの役割)

次のテーブルは

ACL

OBJECT__IDENTITYです。


  • ID


  • OBJECT

    ID

    CLASS:

    ドメインオブジェクトクラスを定義し、____リンク先


ACL

CLASS

テーブル
**

OBJECT

ID

IDENTITY:__ドメインオブジェクトは多くのテーブルに格納できます

クラスによって異なります。したがって、このフィールドにはターゲットオブジェクトが格納されます。
主キー
**

PARENT

OBJECT:

この親の指定

このオブジェクト内のID


**

OWNER

SID:オブジェクト所有者の


ID



ACL

SID

テーブルへのリンク


  • ENTRIES

    INHERITTING:

    このオブジェクトの

    ACLエントリ__を継承するかどうか

親オブジェクトから(

ACLエントリは

ACL

ENTRY

テーブルに定義されています)

最後に、

ACL

ENTRY

ストアの個々の許可は、

Object Identity

の各

SID__に割り当てられます。


  • ID


  • ACL

    OBJECT

    IDENTITY:

    オブジェクトのIDを指定し、リンク先


ACL

OBJECT

IDENTITY

テーブル
**

ACL

ORDER:


ACLエントリ__リスト内の現在のエントリの順序

対応する

Object Identity

**

SID:

パーミッションが許可されているか拒否されているターゲット

SID

から、

ACL

SID

テーブルへのリンク
**

MASK:__実際の許可を表す整数ビットマスク

許可または拒否されている
**

GRANTING:

value 1は許可を意味し、value

0

は拒否を意味します


  • AUDIT

    SUCCESS

    および

    AUDIT

    FAILURE

    :監査目的


2.2. 依存

私たちのプロジェクトで

Spring ACL

を使えるようにするには、まず依存関係を定義しましょう:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-acl</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.11</version>
</dependency>


Spring ACL

には、

Object Identity



ACLエントリを格納するためのキャッシュが必要です。そこで、ここでは

Ehcache

を利用します。そして、

Springで

Ehcache

をサポートするためには、__spring-context-supportも必要です。

Spring Bootを使用しない場合は、バージョンを明示的に追加する必要があります。

それらはMaven Centralで確認できます。


spring-security-acl

、https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.security%22%20AND%20a%3A%22spring-security-config%22[spring-security -config]、https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework%22%20AND%20a%3A%22spring-context-support%22[spring-context -support]、https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22net.sf.ehcache.internal%22%20AND%20a%3A%22ehcache-core%22[ehcache -コア]。


2.3. ACL関連の設定

__Global Method Securityを有効にして、保護されたドメインオブジェクトを返すか、オブジェクトに変更を加えるすべてのメソッドを保護する必要があります。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class AclMethodSecurityConfiguration
  extends GlobalMethodSecurityConfiguration {

    @Autowired
    MethodSecurityExpressionHandler
      defaultMethodSecurityExpressionHandler;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return defaultMethodSecurityExpressionHandler;
    }
}

また、

Spring Expression Language(SpEL)を使用するために

prePostEnabled



true

に設定して

Expression-Based Access Control__を有効にしましょう。

@Bean
public MethodSecurityExpressionHandler
  defaultMethodSecurityExpressionHandler() {
    DefaultMethodSecurityExpressionHandler expressionHandler
      = new DefaultMethodSecurityExpressionHandler();
    AclPermissionEvaluator permissionEvaluator
      = new AclPermissionEvaluator(aclService());
    expressionHandler.setPermissionEvaluator(permissionEvaluator);
    return expressionHandler;
}

したがって、

__、



AclPermissionEvaluator



DefaultMethodSecurityExpressionHandler

に割り当てます。評価者は、データベースから許可設定とドメインオブジェクトの定義をロードするために

MutableAclService__を必要とします。

簡単にするために、提供されている

JdbcMutableAclService

を使用します。

@Bean
public JdbcMutableAclService aclService() {
    return new JdbcMutableAclService(
      dataSource, lookupStrategy(), aclCache());
}

その名前として、

JdbcMutableAclService

はデータベースアクセスを単純化するために

JDBCTemplate

を使用します。

__DataSource(


for

JDBCTemplate)



LookupStrategy

(データベースへの照会時に最適化された検索を提供)、および


AclCache(


caching

ACL

Entries

および

Object Identity)

が必要です。

繰り返しになりますが、わかりやすくするために、

BasicLookupStrategy



EhCacheBasedAclCache

を使用します。

@Autowired
DataSource dataSource;

@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
    return new AclAuthorizationStrategyImpl(
      new SimpleGrantedAuthority("ROLE__ADMIN"));
}

@Bean
public PermissionGrantingStrategy permissionGrantingStrategy() {
    return new DefaultPermissionGrantingStrategy(
      new ConsoleAuditLogger());
}

@Bean
public EhCacheBasedAclCache aclCache() {
    return new EhCacheBasedAclCache(
      aclEhCacheFactoryBean().getObject(),
      permissionGrantingStrategy(),
      aclAuthorizationStrategy()
    );
}

@Bean
public EhCacheFactoryBean aclEhCacheFactoryBean() {
    EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
    ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
    ehCacheFactoryBean.setCacheName("aclCache");
    return ehCacheFactoryBean;
}

@Bean
public EhCacheManagerFactoryBean aclCacheManager() {
    return new EhCacheManagerFactoryBean();
}

@Bean
public LookupStrategy lookupStrategy() {
    return new BasicLookupStrategy(
      dataSource,
      aclCache(),
      aclAuthorizationStrategy(),
      new ConsoleAuditLogger()
    );
}

ここでは、

AclAuthorizationStrategy

が、現在のユーザーが特定のオブジェクトに対するすべての必要な権限を所有しているかどうかを判断します。

特定の

SIDに権限が付与されているかどうかを判断するためのロジックを定義する

PermissionGrantingStrategy、__のサポートが必要です。


3 Spring ACL

によるメソッドセキュリティ

これまでのところ、必要な設定はすべて完了しています。

デフォルトでは、

Spring ACL

は使用可能なすべての権限について

BasePermission

クラスを参照します。基本的に、

READ、WRITE、CREATE、DELETE

、および

ADMINISTRATION

権限があります。

いくつかのセキュリティルールを定義してみましょう。

@PostFilter("hasPermission(filterObject, 'READ')")
List<NoticeMessage> findAll();

@PostAuthorize("hasPermission(returnObject, 'READ')")
NoticeMessage findById(Integer id);

@PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
NoticeMessage save(@Param("noticeMessage")NoticeMessage noticeMessage);


findAll()

メソッドの実行後、

@ PostFilter

がトリガされます。必要なルール

hasPermission(filterObject、 ‘READ’)、

は、現在のユーザーが

READ

権限を持っている

NoticeMessage

のみを返すことを意味します。

同様に、

@ PostAuthorize



findById()メソッドの実行後にトリガされます。現在のユーザが

READ

権限を持っている場合にのみ

NoticeMessage

オブジェクトを返すようにしてください。そうでない場合、システムは

AccessDeniedException__をスローします。

一方、

save()メソッドを呼び出す前に、システムは

@ PreAuthorize

アノテーションをトリガします。対応するメソッドがどこで実行を許可されるかを決定します。そうでなければ、

AccessDeniedException__がスローされます。


4実行中

それでは、

JUnit

を使用してこれらすべての設定をテストします。設定をできる限り単純にするために

H2

データベースを使用します。

追加する必要があります。

<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>


4.1. シナリオ

このシナリオでは、2人のユーザー(

manager、hr)

と1人のユーザーロール(

ROLE

EDITOR)があるので、

acl

sid__は次のようになります。

INSERT INTO acl__sid (id, principal, sid) VALUES
  (1, 1, 'manager'),
  (2, 1, 'hr'),
  (3, 0, 'ROLE__EDITOR');

それから、

acl

class



NoticeMessage

クラスを宣言する必要があります。そして

NoticeMessage

クラスの3つのインスタンスが

system

message.

に挿入されます。

さらに、これら3つのインスタンスに対応するレコードは、

acl

object

identity

で宣言する必要があります。

INSERT INTO acl__class (id, class) VALUES
  (1, 'org.baeldung.acl.persistence.entity.NoticeMessage');

INSERT INTO system__message(id,content) VALUES
  (1,'First Level Message'),
  (2,'Second Level Message'),
  (3,'Third Level Message');

INSERT INTO acl__object__identity
  (id, object__id__class, object__id__identity,
  parent__object, owner__sid, entries__inheriting)
  VALUES
  (1, 1, 1, NULL, 3, 0),
  (2, 1, 2, NULL, 3, 0),
  (3, 1, 3, NULL, 3, 0);

最初に、最初のオブジェクト(

id = 1)に対する

READ

および

WRITE

権限をユーザー

manager

に付与します。一方、

ROLE

EDITOR

を持つユーザーは、3つすべてのオブジェクトに対する

READ

権限を持ちますが、3番目のオブジェクトに対する

WRITE

権限のみを持ちます(

id = 3

)。また、ユーザー

hr

には2番目のオブジェクトに対する

READ

権限しかありません。

ここでは、パーミッションチェックにデフォルトの

Spring ACL


BasePermission

クラスを使用しているので、

READ

パーミッションのマスク値は1になり、

WRITE

パーミッションのマスク値は2になります。

INSERT INTO acl__entry
  (id, acl__object__identity, ace__order,
  sid, mask, granting, audit__success, audit__failure)
  VALUES
  (1, 1, 1, 1, 1, 1, 1, 1),
  (2, 1, 2, 1, 2, 1, 1, 1),
  (3, 1, 3, 3, 1, 1, 1, 1),
  (4, 2, 1, 2, 1, 1, 1, 1),
  (5, 2, 2, 3, 1, 1, 1, 1),
  (6, 3, 1, 3, 1, 1, 1, 1),
  (7, 3, 2, 3, 2, 1, 1, 1);


4.2. テストケース

まず最初に、

findAll

メソッドを呼び出そうとします。

この設定では、このメソッドはユーザーが

READ

権限を持つ

NoticeMessage

のみを返します。

したがって、結果リストには最初のメッセージだけが含まれることになります。

@Test
@WithMockUser(username = "manager")
public void
  givenUserManager__whenFindAllMessage__thenReturnFirstMessage(){
    List<NoticeMessage> details = repo.findAll();

    assertNotNull(details);
    assertEquals(1,details.size());
    assertEquals(FIRST__MESSAGE__ID,details.get(0).getId());
}

それから

ROLE

EDITOR

という役割を持つユーザーで同じメソッドを呼び出そうとします。この場合、これらのユーザーは3つのオブジェクトすべてに対して

READ__権限を持っていることに注意してください。

したがって、結果リストには3つのメッセージすべてが含まれることになります。

@Test
@WithMockUser(roles = {"EDITOR"})
public void
  givenRoleEditor__whenFindAllMessage__thenReturn3Message(){
    List<NoticeMessage> details = repo.findAll();

    assertNotNull(details);
    assertEquals(3,details.size());
}

次に、

manager

ユーザーを使用して、最初のメッセージをIDで取得し、その内容を更新します。これらはすべてうまくいきます。

@Test
@WithMockUser(username = "manager")
public void
  givenUserManager__whenFind1stMessageByIdAndUpdateItsContent__thenOK(){
    NoticeMessage firstMessage = repo.findById(FIRST__MESSAGE__ID);
    assertNotNull(firstMessage);
    assertEquals(FIRST__MESSAGE__ID,firstMessage.getId());

    firstMessage.setContent(EDITTED__CONTENT);
    repo.save(firstMessage);

    NoticeMessage editedFirstMessage = repo.findById(FIRST__MESSAGE__ID);

    assertNotNull(editedFirstMessage);
    assertEquals(FIRST__MESSAGE__ID,editedFirstMessage.getId());
    assertEquals(EDITTED__CONTENT,editedFirstMessage.getContent());
}

しかし、

ROLE

EDITOR

ロールを持つユーザーが最初のメッセージの内容を更新すると、システムは

AccessDeniedException__をスローします。

@Test(expected = AccessDeniedException.class)
@WithMockUser(roles = {"EDITOR"})
public void
  givenRoleEditor__whenFind1stMessageByIdAndUpdateContent__thenFail(){
    NoticeMessage firstMessage = repo.findById(FIRST__MESSAGE__ID);

    assertNotNull(firstMessage);
    assertEquals(FIRST__MESSAGE__ID,firstMessage.getId());

    firstMessage.setContent(EDITTED__CONTENT);
    repo.save(firstMessage);
}

同様に、

hr

ユーザーは2番目のメッセージをIDで見つけることができますが、更新に失敗します。

@Test
@WithMockUser(username = "hr")
public void givenUsernameHr__whenFindMessageById2__thenOK(){
    NoticeMessage secondMessage = repo.findById(SECOND__MESSAGE__ID);
    assertNotNull(secondMessage);
    assertEquals(SECOND__MESSAGE__ID,secondMessage.getId());
}

@Test(expected = AccessDeniedException.class)
@WithMockUser(username = "hr")
public void givenUsernameHr__whenUpdateMessageWithId2__thenFail(){
    NoticeMessage secondMessage = new NoticeMessage();
    secondMessage.setId(SECOND__MESSAGE__ID);
    secondMessage.setContent(EDITTED__CONTENT);
    repo.save(secondMessage);
}


5結論

この記事では、

Spring ACL

の基本構成と使用法について説明しました。

私たちが知っているように、

Spring ACL

はオブジェクト、原則/権限、そして権限設定を管理するために特定のテーブルを必要としました。これらのテーブルとのやり取り、特に更新アクションはすべて

AclServiceを通過する必要があります。今後の記事では、このサービスで基本的な

CRUD__アクションを調べます。

デフォルトでは、

__ BasePermissio

__nクラスで事前定義された許可に制限されています。

最後に、このチュートリアルの実装はhttps://github.com/eugenp/tutorials/tree/master/spring-security-acl[over on Github]にあります。