2012-11-14 9 views
6

Desidero proteggere il livello dei miei servizi utilizzando Spring Security. Come spiegato nella documentazione, ho bisogno di usare un MethodSecurityInterceptor che verificherà se il richiamo del metodo è permesso.MethodSecurityInterceptor per più metodi

Per decidere se un richiamo del metodo di servizio è consentito per un determinato utente, influire sul ruolo richiesto per il metodo richiamato (utilizzando MethodSecurityMetadataSource) non è sufficiente per me poiché dipende anche dai parametri passati al metodo. Come suggerito nella documentazione, posso scrivere un numero personalizzato AccessDecisionVoter e accedere agli argomenti tramite l'oggetto protetto (MethodInvocation in questo caso).

Tuttavia, la mia logica di autorizzazione è diversa tra i metodi. Ad esempio, gli argomenti possono essere diversi tra più metodi e anche la logica di autorizzazione sarà diversa.

vedo due opzioni:

  • posso utilizzare la logica condizionale nel AccessDecisionVoter per determinare il metodo invocato e la logica di autorizzazione ad utilizzare, ma sembra essere una soluzione brutta.
  • Posso definire uno MethodSecurityInterceptor per metodo da proteggere. Secondo la documentazione di Spring, uno strumento MethodSecurityInterceptor è utilizzato per proteggere molti metodi, quindi mi fa pensare che ci sia un altro modo.

La stessa domanda esiste per la decisione di accesso dopo l'invocazione del metodo (utilizzando AfterInvocationProvider).

Quali sono le alternative?

risposta

3

è possibile implementare il proprio annot metodo di protezione azioni basate sulla costruzione di Spring @PreAuthorize("").

per recuperare informazioni aggiuntive sul metodo (al di là dei valori metodo di argomento) al contesto di valutazione SPEL è possibile implementare il proprio 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; 
} 

e registrarlo nella dichiarazione global-method-security

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

Ora può creare annotazioni di sicurezza personalizzate (e dati di annotazione di processo aggiuntivi in ​​MySecurityExpressionHandler se necessario)

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

ad esempio è possibile creare un'annotazione personalizzato per controllare ruoli utente senza fare confusione con le stringhe:

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

ho raggiunto che mediante l'attuazione di mia AccessDecisionManager che i delegati l'accesso decisioni al mio speciale interfaccia AccessDecisionStrategy: strategia decisione

public interface AccessDecisionStrategy { 

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

} 

Ogni accesso rappresenta modo diverso di fare decisione accesso.

Si può facilmente implementare la propria strategia (anche in altra lingua - per esempio Scala):

public class SomeStrategy implements AccessDecisionStrategy { ... 

Come si può vedere, il mio AccessDecisionManager ha una mappa delle strategie. La strategia utilizzata dal gestore si basa sull'argomento di annotazione.

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

Ora, quando voglio proteggere il mio metodo ho messo @Secured annotazione con argomento che è il nome della strategia:

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

È possibile implementare e configurare come molte strategie che vuoi:

Per inserire il gestore delle decisioni di accesso digitare:

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

ho classe di supporto anche implementato per gestire MethodInvocation argomenti:

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

} 

Ora si può facilmente estrarre argomenti interessanti nel codice della vostra strategia di prendere decisioni:

Diciamo che voglio ottenere il numero argomento 0 che è di tipo Long:

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

+1 per il puntatore per lanciare oggetti per MethodInvocation a decidere(). I documenti di Spring mancano di quella parte vitale di informazioni ... –

+0

Nel caso qualcuno ne abbia bisogno, ho aggiunto come implementarlo usando Java Config (il mio progetto usa Spring-Boot) [in questa nuova domanda] (http: // stackoverflow. com/q/24983046/2.796.922). Vedi la mia risposta. – elysch

+0

Per molti casi d'uso non è necessario implementare il proprio decision manager. Invece puoi semplicemente scrivere un metodo e chiamarlo direttamente da '@ Preauthorize', vedi questa risposta: http://stackoverflow.com/a/39972346/1169324 –

Problemi correlati