Come creare metodi personalizzati da utilizzare nelle annotazioni del linguaggio delle espressioni di sicurezza primaverili


92

Vorrei creare una classe che aggiunge metodi personalizzati da utilizzare nel linguaggio delle espressioni di sicurezza primaverili per l'autorizzazione basata su metodo tramite annotazioni.

Ad esempio, vorrei creare un metodo personalizzato come 'customMethodReturningBoolean' da utilizzare in qualche modo in questo modo:

  @PreAuthorize("customMethodReturningBoolean()")
  public void myMethodToSecure() { 
    // whatever
  }

La mia domanda è questa. Se è possibile, quale classe dovrei sottoclassare per creare i miei metodi personalizzati, come dovrei configurarlo nei file di configurazione xml di primavera e qualcuno mi dia un esempio di un metodo personalizzato utilizzato in questo modo?


1
Non ho tempo per digitare una risposta in questo momento, ma ho seguito questa guida e ha funzionato perfettamente : baeldung.com/… Sto usando Spring Security 5.1.1.
Paul

Risposte:


35

Avrai bisogno di sottoclassare due classi.

Per prima cosa, imposta un nuovo gestore di espressioni del metodo

<global-method-security>
  <expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>

myMethodSecurityExpressionHandlersarà una sottoclasse di DefaultMethodSecurityExpressionHandlerquali sostituzioni createEvaluationContext(), impostando una sottoclasse MethodSecurityExpressionRootsul MethodSecurityEvaluationContext.

Per esempio:

@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
    MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
    MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
    root.setTrustResolver(trustResolver);
    root.setPermissionEvaluator(permissionEvaluator);
    root.setRoleHierarchy(roleHierarchy);
    ctx.setRootObject(root);

    return ctx;
}

Hmm, sembra una buona idea, ma tutte le proprietà di DefaultMethodSecurityExpressionHandler sono private senza accessors, quindi ero curioso di sapere come hai esteso la classe senza alcun brutto riflesso. Grazie.
Joseph Lust

1
Intendi trustResolver, ecc.? Tutti hanno setter in DefaultMethodSecurityExpressionHandler (almeno in Spring Security 3.0) Vedi: static.springsource.org/spring-security/site/apidocs/org/…
sourcedelica

3
@ericacm Come ci si aggira per MethodSecurityExpressionRootessere privati ​​dei pacchetti ?
C. Ross

175

Nessuna delle tecniche menzionate funzionerà più. Sembra che Spring abbia fatto di tutto per impedire agli utenti di sovrascrivere SecurityExpressionRoot.

EDIT 19/11/14 Imposta Spring per utilizzare le annotazioni di sicurezza:

<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... >
...
<sec:global-method-security pre-post-annotations="enabled" />

Crea un fagiolo come questo:

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(String key) {
        return true;
    }
}

Quindi fai qualcosa di simile nel tuo jsp:

<sec:authorize access="@mySecurityService.hasPermission('special')">
    <input type="button" value="Special Button" />
</sec:authorize>

Oppure annota un metodo:

@PreAuthorize("@mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }

Inoltre, puoi usare Spring Expression Language nelle tue @PreAuthorizeannotazioni per accedere all'autenticazione corrente e agli argomenti del metodo.

Per esempio:

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(Authentication authentication, String foo) { ... }
}

Quindi aggiorna il tuo @PreAuthorizein modo che corrisponda alla nuova firma del metodo:

@PreAuthorize("@mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }

6
@Bosh nel tuo metodo hasPermission, puoi usare Authentication auth = SecurityContextHolder.getContext().getAuthentication();per ottenere il token di autenticazione corrente.
James Watkins,

2
Grazie James per la tua risposta. Devo definire mySecurityService nel file di configurazione primaverile?
WowBow

2
Non è necessario definire mySecurityService in alcun file XML se si dispone di una configurazione di scansione dei componenti per il pacchetto in cui si trova il servizio. Se non si dispone di una scansione dei componenti corrispondente, è necessario utilizzare una definizione di bean xml. @PreAuthorize viene da org.springframework.security
James Watkins

3
Potrebbe essere necessario specificare il nome del bean per l'annotazione in questo modo: @Component ("mySecurityService") o utilizzare l'annotazione @Named.
James Watkins

1
@ VJS Si prega di vedere la modifica che ho fatto. Sarà necessario configurare la molla per utilizzare queste annotazioni. Sono sorpreso che nessun altro si sia lamentato di questo importante dettaglio mancante :)
James Watkins

14

Grazie ericacm , ma non funziona per alcuni motivi:

  • Le proprietà di DefaultMethodSecurityExpressionHandler sono private (la visibilità della riflessione kludges è indesiderabile)
  • Almeno nel mio Eclipse, non riesco a risolvere un oggetto MethodSecurityEvaluationContext

Le differenze sono che chiamiamo il metodo createEvaluationContext esistente e quindi aggiungiamo il nostro oggetto root personalizzato. Infine ho appena restituito un tipo di oggetto StandardEvaluationContext poiché MethodSecurityEvaluationContext non si risolveva nel compilatore (sono entrambi dalla stessa interfaccia). Questo è il codice che ora ho in produzione.

Fai in modo che MethodSecurityExpressionHandler utilizzi la nostra radice personalizzata:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler  {

    // parent constructor
    public CustomMethodSecurityExpressionHandler() {
        super();
    }

    /**
     * Custom override to use {@link CustomSecurityExpressionRoot}
     * 
     * Uses a {@link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
     * configures it with a {@link MethodSecurityExpressionRoot} instance as the expression root object.
     */
    @Override
    public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
        // due to private methods, call original method, then override it's root with ours
        StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
        ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
        return ctx;
    }
}

Questo sostituisce la radice predefinita estendendo SecurityExpressionRoot . Qui ho rinominato hasRole in hasEntitlement:

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot  {

    // parent constructor
    public CustomSecurityExpressionRoot(Authentication a) {
        super(a);
    }

    /**
     * Pass through to hasRole preserving Entitlement method naming convention
     * @param expression
     * @return boolean
     */
    public boolean hasEntitlement(String expression) {
        return hasRole(expression);
    }

}

Infine aggiorna securityContext.xml (e assicurati che sia referenziato dal tuo applcationContext.xml):

<!-- setup method level security using annotations -->
<security:global-method-security
        jsr250-annotations="disabled"
        secured-annotations="disabled"
        pre-post-annotations="enabled">
    <security:expression-handler ref="expressionHandler"/>
</security:global-method-security>

<!--<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">-->
<bean id="expressionHandler" class="com.yourSite.security.CustomMethodSecurityExpressionHandler" />

Nota: l'annotazione @Secured non accetterà questa sostituzione poiché viene eseguita attraverso un gestore di convalida diverso. Quindi, nell'XML sopra li ho disabilitati per evitare confusione successiva.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.