2014-11-05 17 views
22

C'è un modo per creare più espressioni espressive nei blocchi @Preauthorize? Ecco un esempio di qualcosa che mi ritrovo a ripetere, perché @Preauthorize non è terribilmente intelligente fuori dagli schemi.Come utilizzare le espressioni personalizzate in Spring Security @ PreAuthorize/@ PostAuthorize annotazioni

@RequestMapping(value = "{id}", method = RequestMethod.DELETE) 
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser) { 
    Game currentGame = gameService.findById(id); 
    if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) { 
     gameService.delete(gameService.findById(id)); 
    } else { 
     throw new SecurityException("Only an admin, or an owner can delete a game."); 
    } 
} 

Quello che preferirei è qualcosa di simile.

@RequestMapping(value = "{id}", method = RequestMethod.DELETE) 
@Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id) 
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser, @ModelAttribute currentGame) { //I'm not sure how to add this either :(
    gameService.delete(gameService.findById(id)); 
} 

Parte del problema è che ho bisogno di fare una query al database per andare a prendere un po 'di questa roba per verificare le autorizzazioni, come interrogare il database per ottenere una copia del gioco, e poi confrontando il proprietario del gioco alla persona che effettua la richiesta. Non sono davvero sicuro di come tutto funzioni all'interno del contesto di un processore di annotazione @Preauthorize, o di come aggiungo cose alla raccolta di oggetti resi disponibili nell'attributo di valore @Preauthorize ("").

risposta

20

1) Per prima cosa è necessario reimplementare MethodSecurityExpressionRoot che contiene funzionalità aggiuntive specifiche del metodo. L'implementazione originale di Spring Security è un pacchetto privato e quindi non è possibile estenderlo. Suggerisco di controllare il codice sorgente per la classe data.

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations { 

    // copy everything from the original Spring Security MethodSecurityExpressionRoot 

    // add your custom methods 

    public boolean isAdmin() { 
     // do whatever you need to do, e.g. delegate to other components 

     // hint: you can here directly access Authentication object 
     // via inherited authentication field 
    } 

    public boolean isOwner(Long id) { 
     // do whatever you need to do, e.g. delegate to other components 
    } 
} 

2) Poi si deve implementare personalizzato MethodSecurityExpressionHandler che utilizzerà il sopra definito CustomMethodSecurityExpressionRoot.

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler { 

    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); 

    @Override 
    public void setReturnObject(Object returnObject, EvaluationContext ctx) { 
     ((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject); 
    } 

    @Override 
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, 
     MethodInvocation invocation) { 
     final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication); 
     root.setThis(invocation.getThis()); 
     root.setPermissionEvaluator(getPermissionEvaluator()); 
     root.setTrustResolver(this.trustResolver); 
     root.setRoleHierarchy(getRoleHierarchy()); 

     return root; 
    } 
} 

3) Definire il bean di gestore espressioni nel proprio contesto, ad es. tramite XML si può fare come segue

<bean id="methodSecurityExpressionHandler" 
    class="my.package.CustomMethodSecurityExpressionHandler"> 
    <property name="roleHierarchy" ref="roleHierarchy" /> 
    <property name="permissionEvaluator" ref="permissionEvaluator" /> 
</bean> 

4) Registrare il gestore di sopra definito

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

5) Poi basta utilizzare le espressioni definite nei tuoi @PreAuthorize e/o @PostAuthorize annotazioni

@PreAuthorize("isAdmin() or isOwner(#id)") 
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) { 
    // do whatever needed 
} 

E ancora una cosa. Non è molto comune utilizzare la sicurezza a livello di metodo per proteggere i metodi del controller, ma piuttosto per proteggere i metodi con la logica aziendale (a.k.a. i metodi del livello di servizio). Quindi potresti usare qualcosa come il seguente.

public interface GameService { 

    // rest omitted 

    @PreAuthorize("principal.admin or #game.owner = principal.username") 
    public void delete(@P("game") Game game); 
} 

Ma tenere presente che questo è solo un esempio. Si aspetta che l'attuale principale abbia il metodo isAdmin() e che il gioco abbia il metodo getOwner() che restituisce il nome utente del proprietario.

+0

Questa soluzione mi ha aiutato con avvio primavera 1.3.3.RELEASE – ali

7

Si potrebbe scrivere l'annotazione qualcosa di simile:

@PreAuthorize("hasRole('ROLE_ADMIN') and hasPermission(#id, 'Game', 'DELETE')") 

Per ottenere la parte hasPermission lavoro è necessario implementare PermissionEvaluator interfaccia.

quindi definire un bean gestore espressione:

@Autowired 
private PermissionEvaluator permissionEvaluator; 

@Bean 
public DefaultMethodSecurityExpressionHandler expressionHandler() 
{ 
    DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); 
    handler.setPermissionEvaluator(permissionEvaluator); 
    return handler; 
} 

e iniettare nella configurazione di sicurezza:

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

Dal @PreAuthorize viene valutata SpEl -expressions, il modo più semplice è solo per puntare a un fagiolo:

@PreAuthorize("@mySecurityService.someFunction()") 

MySecurityService.someFunction avere il tipo di ritorno boolean.

Spring-security fornirà automaticamente una variabile denominata authentication se si desidera passare l'oggetto Authentication. È inoltre possibile utilizzare qualsiasi SPEL-espressioni valide per accedere a tutti i parametri passati al metodo sicuro, valutare le espressioni regolari, chiamare i metodi statici, ecc Ad esempio:

@PreAuthorize("@mySecurityService.someFunction(authentication, #someParam)") 
+0

Questa mi sembra un'ottima soluzione. È possibile creare un numero di @Components che contengono il proprio codice di autorizzazione personalizzato che può essere riutilizzato tra gli endpoint – ashario

Problemi correlati