web-dev-qa-db-ja.com

Spring Security @ PreAuthorize / @ PostAuthorizeアノテーションでカスタム式を使用する方法

@Preauthorizeブロックでより表現力豊かなステートメントを作成する方法はありますか? @Preauthorizeはすぐに使えるほどスマートではないので、ここに私が繰り返すことの例を示します。

@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser) {
    Game currentGame = gameService.findById(id);
    if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) {
        gameService.delete(gameService.findById(id));
    } else {
        throw new SecurityException("Only an admin, or an owner can delete a game.");
    }
}

私が好むのは次のようなものです。

@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
@Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id)
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser, @ModelAttribute currentGame ) { //I'm not sure how to add this either :(
   gameService.delete(gameService.findById(id));
}

問題の一部は、データベースに照会してゲームのコピーを取得し、ゲームの所有者と作成者を比較するなど、権限を確認するためにデータベースに照会する必要があることです。リクエスト。これらすべてが@Preauthorizeアノテーションプロセッサのコンテキスト内でどのように動作するのか、または@Preauthorize( "")value属性で使用可能になったオブジェクトのコレクションに物事を追加するのかは、本当にわかりません。

27
Jazzepi

1)まず、メソッド固有の追加機能を含むMethodSecurityExpressionRootを再実装する必要があります。元のSpring Security実装はパッケージプライベートであるため、単に拡張することはできません。特定のクラスのソースコードを確認することをお勧めします。

_public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    // copy everything from the original Spring Security MethodSecurityExpressionRoot

    // add your custom methods

    public boolean isAdmin() {
        // do whatever you need to do, e.g. delegate to other components

        // hint: you can here directly access Authentication object 
        // via inherited authentication field
    }

    public boolean isOwner(Long id) {
        // do whatever you need to do, e.g. delegate to other components
    }
}
_

2)次に、上記で定義したMethodSecurityExpressionHandlerを使用するカスタムCustomMethodSecurityExpressionRootを実装する必要があります。

_public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

    @Override
    public void setReturnObject(Object returnObject, EvaluationContext ctx) {
        ((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
    }

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
        MethodInvocation invocation) {
        final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
        root.setThis(invocation.getThis());
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());

        return root;
    }
}
_

3)コンテキストで式ハンドラーBeanを定義します。 XMLを介して次のように実行できます

_<bean id="methodSecurityExpressionHandler"
    class="my.package.CustomMethodSecurityExpressionHandler">
    <property name="roleHierarchy" ref="roleHierarchy" />
    <property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
_

4)上記で定義したハンドラーを登録する

_<security:global-method-security pre-post-annotations="enabled">
    <security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>
_

5)次に、_@PreAuthorize_および/または_@PostAuthorize_注釈で定義された式を使用します

_@PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) {
    // do whatever needed
}
_

後もう一つ。メソッドレベルのセキュリティを使用してコントローラーメソッドをセキュリティで保護することはあまり一般的ではなく、ビジネスロジック(サービスレイヤーメソッド)を使用してメソッドをセキュリティで保護することは一般的です。次に、以下のようなものを使用できます。

_public interface GameService {

    // rest omitted

    @PreAuthorize("principal.admin or #game.owner = principal.username")
    public void delete(@P("game") Game game);
}
_

ただし、これは単なる例にすぎないことに注意してください。実際のプリンシパルにはisAdmin()メソッドがあり、ゲームにはgetOwner()メソッドがあり、所有者のユーザー名を返すことが期待されています。

28
pgiecek

@PreAuthorizeSpEl -expressionsを評価します。最も簡単な方法は、Beanを指すことです。

    @PreAuthorize("@mySecurityService.someFunction()")

MySecurityService.someFunctionは戻り型booleanを持っている必要があります。

authentication-オブジェクトを渡す場合、Spring-securityはAuthenticationという名前の変数を自動的に提供します。有効なSpEl式を使用して、セキュアメソッドに渡された引数にアクセスしたり、正規表現を評価したり、静的メソッドを呼び出したりすることもできます。例:

    @PreAuthorize("@mySecurityService.someFunction(authentication, #someParam)")
50
gogstad

次のような注釈を書くことができます。

@PreAuthorize("hasRole('ROLE_ADMIN') and hasPermission(#id, 'Game', 'DELETE')")

HasPermissionパーツを機能させるには、 PermissionEvaluator インターフェイスを実装する必要があります。

次に、式ハンドラーBeanを定義します。

@Autowired
private PermissionEvaluator permissionEvaluator;

@Bean
public DefaultMethodSecurityExpressionHandler expressionHandler()
{
    DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
    handler.setPermissionEvaluator(permissionEvaluator);
    return handler;
}

そして、セキュリティ設定を挿入します:

<global-method-security pre-post-annotations="enabled">
  <expression-handler ref="expressionHandler" />
</global-method-security>
10
holmis83