アプリケーションセキュリティのGroovyおよびAST変換

背景



Grailsで小さなポータルを開発し、Spring Securityを使用してセキュリティを管理しています。 Grailsのスプリングセキュリティプラグインは非常に便利で、最後の瞬間まで複雑な機能を必要としませんでした。



Grailsコントローラーメソッドに@Securedアノテーションを使用しているときに、不愉快な瞬間が最近発見されました。 問題は、注釈が実行時に処理され、アドレスの一連のルール「アドレス->必要なロールのセット」に変換されることです。 このアプローチでは、Grailsコントローラーのデータストレージ/削除アクションで多くの問題が発生します。これは、コントローラーのメインURLにデータを送信するためです。まずコントローラーに注釈を付ける必要があります。



問題を解決し、セキュリティルールを管理するための優れたツールを取得する方法についてです。



可能な解決策



Grailsのプラグインの開発者がユーザーに関してそれほど怠慢な理由を理解していません。おそらくユーザーにとっては簡単だったでしょう。



代替ソリューション:



2番目の方法は、アセンブリ手順を整理する必要があるため、Javaで実装するのは好ましくありません。 しかし、Groovyではそうではありません。 Groovyでは、このような目的のためにメタプログラミングまたはAST変換(Abastract Syntax Tree)が一般的に使用されます。



コントローラへのリクエストをフィルタリングするためのメタプログラミングは、安定したソリューションというよりもハックのように見えるため、考慮しません。



変換



変換は、Grailsで広く使用されており、たとえば、モデルクラスにidおよびversionフィールドを追加します。 これらを使用して、コントローラーメソッドへの呼び出しをフィルター処理します。



変換は、ASTTransformationインターフェースと注釈付きの@GroovyASTTransformationを実装する単純なJavaクラスです。これには、1つのvisitメソッドのみが含まれます。実際、Visitorパターンの典型的な代表です。 変換の場合、適用されるコンパイルフェーズを指定できます。 そして、訪問者に到着するノードを選択するには、GroovyASTTransformationClassで注釈が付けられた注釈クラスが必要です。 その結果、変換は構文ツリーを変更し、ノードを追加/変更/削除し、結果のバイトコードに影響を与えます。



合計で、注釈と変換クラスが必要です。 簡単にするために、それらを@SuperSecuredおよびSuperSecuredTransformationと呼びましょう。



要約には、文字列の配列の値(メソッドにアクセスするために必要なロール)が含まれます。

@Retention(RetentionPolicy.SOURCE)-最終バイトコードに注釈が存在しないことを示します。

package com.example; import org.codehaus.groovy.transform.GroovyASTTransformationClass; import java.lang.annotation.*; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) @GroovyASTTransformationClass("com.example.SuperSecuredTransformation") public @interface SuperSecured { String[] value() default {}; }
      
      







変換:

 package com.example; import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.*; import org.codehaus.groovy.control.*; import org.codehaus.groovy.transform.*; import java.util.List; @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) public class SuperSecuredTransformation implements ASTTransformation { @Override public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) { if (astNodes != null) { for (ASTNode node : astNodes) { if (node instanceof MethodNode) { MethodNode methodNode = (MethodNode) node; List<AnnotationNode> annotations = methodNode.getAnnotations(new ClassNode(SuperSecured.class)); if (annotations != null && !annotations.isEmpty()) { injectRolesCheck(methodNode, annotations); } } } } } private void injectRolesCheck(MethodNode method, List<AnnotationNode> annotations) { for (AnnotationNode annotationNode : annotations) { BlockStatement code = (BlockStatement) method.getCode(); Expression rolesValue = annotationNode.getMember("value"); Expression checkRolesExpression = new StaticMethodCallExpression( new ClassNode(SuperSecuredInspector.class), "rejectByRoles", new ArgumentListExpression( rolesValue ) ); code.getStatements().add(0, new ExpressionStatement(checkRolesExpression)); } } }
      
      





次のことが判明しました:変換は@SuperSecuredとして注釈が付けられた構文ツリーノードを取得し、メソッドである場合、注釈値のロールのリストを使用して静的メソッドSuperSecuredInspector.rejectByRolesへの呼び出しを先頭に追加します。 現在のユーザーがセキュリティ条件を満たさない場合、このメソッドはAccessDeniedExceptionをスローします。



最終的にそのような注釈を使用することは喜びです。



おわりに



このアプローチにより、オブジェクトにアクセスするための複雑なルールを識別し、コード内でそれらを複製することはできません。 注釈は非常に表現力があり、コード生成は静的に型付けされているため、実行中のエラーを回避できます。



変換は、GroovyのAOPにふさわしい代替手段です。



参照資料







PS Habra開発者は、ソースタグの@記号の表示を修復してください。



All Articles