2012-11-14 13 views
6

Ich möchte meine Services Layer mit Spring Security sichern. Wie in der Dokumentation erklärt, muss ich eine MethodSecurityInterceptor verwenden, die überprüft, ob der Methodenaufruf erlaubt ist.MethodSecurityInterceptor für mehrere Methoden

Um zu entscheiden, ob ein Aufruf der Servicemethode für einen bestimmten Benutzer zulässig ist, ist eine Auswirkung auf eine erforderliche Rolle für die aufgerufene Methode (MethodSecurityMetadataSource) nicht ausreichend, da sie auch von den an die Methode übergebenen Parametern abhängt. Wie in der Dokumentation vorgeschlagen, kann ich eine benutzerdefinierte AccessDecisionVoter schreiben und auf die Argumente über das gesicherte Objekt zugreifen (MethodInvocation in diesem Fall).

Aber meine Autorisierungslogik unterscheidet sich in den Methoden. Zum Beispiel können die Argumente zwischen mehreren Methoden unterschiedlich sein und die Berechtigungslogik wird auch anders sein.

Ich sehe zwei Möglichkeiten:

  • I bedingte Logik in den AccessDecisionVoter kann die aufgerufene Methode und die Berechtigungslogik zu bestimmen, zu verwenden, aber es scheint eine hässliche Lösung.
  • Ich kann eine MethodSecurityInterceptor pro Methode zu sichern. Laut der Spring-Dokumentation wird eine MethodSecurityInterceptor verwendet, um viele Methoden zu sichern, so dass ich denke, dass es einen anderen Weg gibt.

Die gleiche Frage existiert für die Zugriffsentscheidung nach Methodenaufruf (mit AfterInvocationProvider).

Was sind die Alternativen?

Antwort

3

Sie können Ihre eigene Methode Sicherheit annot implementieren auf der Grundlage von Feder @PreAuthorize("") Konstruktion.

zusätzliche Informationen über das Verfahren zu holen (über Werte Methode Argument) zu Spel Auswertung Kontext Sie Ihre eigenen 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; 
} 

und registrieren Sie es in Ihrer global-method-security Erklärung Sie

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

Jetzt implementieren kann benutzerdefinierte Sicherheitsanmerkungen erstellen (und zusätzliche Prozessanmerkungsdaten, falls erforderlich, in MySecurityExpressionHandler)

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

zum Beispiel können Sie eine benutzerdefinierte Anmerkung erstellen, ohne mit Streichern Messing Benutzerrollen zu überprüfen:

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

ich erreicht, dass meine eigene durch die Umsetzung AccessDecisionManager, dass die Delegierten Entscheidungen auf meine spezielle Schnittstelle zugreifen AccessDecisionStrategy:

public interface AccessDecisionStrategy { 

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

} 

Jede Zugriffsentscheidungsstrategie stellt unterschiedliche Art und Weise Zugang Entscheidung zu treffen.

Sie können Ihre eigene Strategie (auch in einer anderen Sprache - zum Beispiel Scala) leicht implementieren:

public class SomeStrategy implements AccessDecisionStrategy { ... 

Wie Sie sehen können, mein AccessDecisionManager hat eine Karte von Strategien. Die vom Manager verwendete Strategie basiert auf dem Annotationsargument.

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

Nun, wenn ich möchte, dass meine Methode, die ich @Secured Anmerkung mit dem Argument setzen schützen, die Namen der Strategie ist:

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

Sie implementieren können und so viele Strategien konfigurieren, wie Sie wollen:

<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> 

Zum Eingeben des Zugriffsentscheidungsmanagers geben Sie Folgendes ein:

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

ich auch implementiert Hilfsklasse MethodInvocation Argumente behandeln:

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

} 

Jetzt können Sie leicht interessante Argumente im Code Ihrer Strategie extrahieren machen Entscheidung:

Sagen wir, ich möchte Argument Nummer bekommen 0, die vom Typ ist Long:

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

+1 für den Zeiger Objekt zu MethodInvocation werfen in entscheiden(). Die Spring-Dokumentation fehlt das wichtige Stück Info ... –

+0

Falls jemand es benötigt, habe ich hinzugefügt, wie man dies mit Java Config (mein Projekt verwendet Spring-Boot) [in dieser neuen Frage] (http: // stackoverflow. com/q/24983046/2796922). Siehe meine Antwort. – elysch

+0

Für viele Anwendungsfälle muss kein eigener Decisionmanager implementiert werden. Stattdessen können Sie einfach eine Methode schreiben und sie direkt von '@ Preauthorize' aufrufen, siehe folgende Antwort: http://stackoverflow.com/a/39972346/1169324 –

Verwandte Themen