7

Spring Security を使用してサービス層を保護したいと考えています。ドキュメントで説明されているMethodSecurityInterceptorように、メソッド呼び出しが許可されているかどうかを確認する を使用する必要があります。

特定のユーザーにサービスメソッドの呼び出しが許可されているかどうかを判断するにはMethodSecurityMetadataSource、メソッドに渡されるパラメーターにも依存するため、呼び出されたメソッドに必要なロールに影響を与える (を使用) だけでは十分ではありません。ドキュメントで提案されているように、カスタムAccessDecisionVoterを作成し、保護されたオブジェクト (MethodInvocationこの場合) を介して引数にアクセスできます。

しかし、私の承認ロジックはメソッドによって異なります。たとえば、複数のメソッド間で引数が異なる場合があり、承認ロジックも異なります。

2 つのオプションが表示されます。

  • で条件付きロジックを使用してAccessDecisionVoter、呼び出されたメソッドと使用する承認ロジックを決定できますが、それは醜い解決策のようです。
  • MethodSecurityInterceptorセキュリティを確保するために、メソッドごとに1 つ定義できます。Spring のドキュメントによると、aMethodSecurityInterceptorは多くのメソッドを保護するために使用されているため、別の方法があると考えさせられます。

メソッド呼び出し後のアクセス決定についても同じ問題があります ( を使用AfterInvocationProvider)。

代替手段は何ですか?

4

2 に答える 2

4

AccessDecisionManagerアクセスの決定を特別なインターフェイスに委譲する独自の実装を行うことで、これを実現しましたAccessDecisionStrategy

public interface AccessDecisionStrategy {

    void decide(Authentication authentication, MethodInvocation methodInvocation, ConfigAttribute configAttribute);

}

各アクセス決定戦略は、アクセス決定を行うさまざまな方法を表しています。

独自の戦略を簡単に実装できます (他の言語でも - たとえば Scala など)。

public class SomeStrategy implements AccessDecisionStrategy { ...

ご覧のとおり、myAccessDecisionManagerには戦略のマップがあります。manager が使用する戦略は、注釈引数に基づいています。

public class MethodSecurityAccessDecisionManager implements AccessDecisionManager {

    private Map<String, AccessDecisionStrategy> strategyMap;

    public MethodSecurityAccessDecisionManager(Map<String, AccessDecisionStrategy> strategyMap) {
        this.strategyMap = strategyMap;
    }

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        ConfigAttribute configAttribute = getSingleConfigAttribute(configAttributes);
        AccessDecisionStrategy accessDecisionStrategy = strategyMap.get(configAttribute.getAttribute());
        if (accessDecisionStrategy == null) {
            throw new IllegalStateException("AccessDecisionStrategy with name "
                    + configAttribute.getAttribute() + " was not found!");
        }
        try {
            accessDecisionStrategy.decide(authentication, (MethodInvocation) object, configAttribute);
        } catch (ClassCastException e) {
            throw new IllegalStateException();
        }
    }

    private ConfigAttribute getSingleConfigAttribute(Collection<ConfigAttribute> configAttributes) {
        if (configAttributes == null || configAttributes.size() != 1) {
            throw new IllegalStateException("Invalid config attribute configuration");
        }
        return configAttributes.iterator().next();
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.equals(MethodInvocation.class);
    }
}

メソッドを保護したい場合@Securedは、戦略の名前である引数を使用して注釈を付けます。

@Secured("GetByOwner")
FlightSpotting getFlightSpotting(Long id);

必要な数の戦略を実装および構成できます。

<bean id="methodSecurityAccessDecisionManager"
      class="some.package.MethodSecurityAccessDecisionManager">

    <constructor-arg>
        <map>
            <entry key="GetByOwner">
                <bean class="some.package.GetByOwnerStrategy"/>
            </entry>

            <entry key="SomeOther">
                <bean class="some.package.SomeOtherStrategy"/>
            </entry>
        </map>
    </constructor-arg>

</bean>

そのアクセス決定マネージャーを挿入するには、次のように入力します。

<sec:global-method-security secured-annotations="enabled"
                            access-decision-manager-ref="methodSecurityAccessDecisionManager">
</sec:global-method-security>

MethodInvocation引数を処理するヘルパー クラスも実装しました。

import org.aopalliance.intercept.MethodInvocation;

public class MethodInvocationExtractor<ArgumentType> {

    private MethodInvocation methodInvocation;

    public MethodInvocationExtractor(MethodInvocation methodInvocation) {
        this.methodInvocation = methodInvocation;
    }

    public ArgumentType getArg(int num) {
        try {
            Object[] arguments = methodInvocation.getArguments();
            return (ArgumentType) arguments[num];
        } catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
            throw new IllegalStateException();
        }
    }

}

これで、戦略のコードで興味深い引数を簡単に抽出して決定を下すことができます。

0タイプの引数番号を取得したいとしましょうLong

MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation);
Long id = extractor.getArg(0);
于 2012-11-14T14:40:17.510 に答える
3

@PreAuthorize("")Spring の構築に基づいて、独自のメソッド セキュリティ アノテーションを実装できます。

メソッドに関する追加情報 (メソッド引数値以外) を SpEL 評価コンテキストに取得するには、独自の MethodSecurityExpressionHandler を実装できます。

@Service
public class MySecurityExpressionHandler extends
    DefaultMethodSecurityExpressionHandler {

    @Override
    public StandardEvaluationContext createEvaluationContextInternal(
        Authentication auth, MethodInvocation mi) {

    StandardEvaluationContext evaluationContext = super
            .createEvaluationContextInternal(auth, mi);

    SomeMethodInfoData methodInfoData = mi.getMethod(). ...;

    evaluationContext.setVariable("someData", <value computed based on method info data>);
    }

    return evaluationContext;
} 

global-method-securityそしてそれをあなたの宣言に登録してください

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

カスタム セキュリティ アノテーションを作成できるようになりました (必要に応じて MySecurityExpressionHandler 内の追加のプロセス アノテーション データも作成できます)。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("#<someData>")
public @interface CustomSecurityAnnotation { ... }

たとえば、カスタム アノテーションを作成して、文字列をいじらずにユーザー ロールをチェックできます。

@MyUserRoleCheck(MyAppRole.Admin)
public void someMethod()
于 2012-11-16T16:25:41.050 に答える