2012-11-14 5 views
6

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

特定のユーザーに対してサービスメソッドの呼び出しが許可されているかどうかを判断するには、呼び出されたメソッド(MethodSecurityMetadataSourceを使用)に必要な役割に影響を与えることは、メソッドに渡されるパラメーターにも依存します。ドキュメントで示唆されているように、私はカスタムAccessDecisionVoterを書いて、保護されたオブジェクト(ここではMethodInvocation)を介して引数にアクセスできます。

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

私は2つのオプションを参照してください。私が使用して呼び出されたメソッドおよび承認ロジックを決定するためにAccessDecisionVoterに条件ロジックを使用することができます

  • を、醜いソリューションのようです。
  • セキュアにする方法ごとに1つ定義することができます。MethodSecurityInterceptor Springのドキュメントによると、MethodSecurityInterceptorは多くのメソッドを保護するために使用されているので、別の方法があると私は考えています。

メソッド呼び出し後のアクセス決定(AfterInvocationProviderを使用)と同じ質問があります。

代替手段はありますか?

答えて

3

あなたがあなた自身のメソッドのセキュリティANNOTを実装することができますタイプLongである0スプリング@PreAuthorize("")の構造に基づくアトオン。あなたがあなた自身の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> 

であなたにそれを登録することができSPEL評価コンテキストに(メソッドの引数の値を超えた)方法に関する追加情報を取得するために

カスタムセキュリティ注釈(および必要に応じてMySecurityExpressionHandler内の追加のプロセス注釈データ)を作成できます

0あなたは、文字列をいじりせずにユーザーの役割を確認するために、カスタムアノテーションを作成することができます例えば

@MyUserRoleCheck(MyAppRole.Admin) 
public void someMethod() 
4

は、私は自分のAccessDecisionManagerを実装することで、参加者は私の特別なインタフェースAccessDecisionStrategyに意思決定をアクセスすることをことを達成:

public interface AccessDecisionStrategy { 

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

} 

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

public class SomeStrategy implements AccessDecisionStrategy { ... 

あなたが見ることができるように、私のAccessDecisionManagerは戦略のマップを持っています -

あなたは簡単に(例えばScalaのためにも、他の言語で)独自の戦略を実装することができます。マネージャが使用する戦略は注釈引数に基づいています。私は私の方法を保護したいとき

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(); 
     } 
    } 

} 

を:

のは、私は引数の数を取得したいとしましょう

MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation); 
Long id = extractor.getArg(0); 
+1

+1決定にMethodInvocationにオブジェクトをキャストするポインタのために()。 Springのドキュメントには、その重要な情報が欠落しています。 –

+0

誰かがそれを必要とする場合は、Java Config(このプロジェクトではSpring-Bootを使用しています)を使用してこれを実装する方法を追加しました(http:// stackoverflow。 com/q/24983046/2796922)。私の答えを見てください。 – elysch

+0

多くのユースケースでは、独自のデシジョンマネージャを実装する必要はありません。代わりに '@ Preauthorize'から直接呼び出すメソッドを書くことができます。この回答を参照してください:http://stackoverflow.com/a/39972346/1169324 –