2013-05-03 14 views
7

Sto aggiungendo Apache Shiro alla mia domanda e mi chiedo se il seguente messaggio di errore è veramente accurata:Un SecurityManager non associato è davvero una configurazione di applicazione non valida in Shiro?

org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessibile al codice chiamante, sia legato a il org.apache.shiro.util.ThreadContext o come un singleton vm statico. Questa è una configurazione di applicazione non valida.

Ho guardato attraverso il codice sorgente di un po 'e l'impressione che ho è che fino a quando non sto usando SecurityUtils e io sono disposto a passare un SecurityManager ai componenti che ne hanno bisogno, io don In realtà, è necessario assegnare lo SecurityManager al singleton statico utilizzato da SecurityUtils.

La cosa specifica che voglio evitare è che Shiro inserisca qualsiasi cosa nello ThreadLocal o che abbia Shiro che usa la sua classe di supporto ThreadContext. Sto usando Apache Thrift e non voglio impegnarmi in un design di rete a thread singolo per richiesta. Le mie richieste di Shiro sono piuttosto ridotte, quindi mostrerò cosa sto facendo qui sotto.

Sto utilizzando Guice nella mia richiesta, ma sono non utilizzando shiro-guice perché la roba Shiro AOP dipende da avere un Subject associato ad un ThreadContext. Invece, comincio con un modulo Guice estremamente semplice.

public class ShiroIniModule extends AbstractModule { 
    @Override 
    protected void configure() {} 

    @Provides 
    @Singleton 
    public SecurityManager provideSecurityManager() { 
     return new DefaultSecurityManager(new IniRealm("classpath:shiro.ini")); 
    } 
} 

Questo non è esattamente un setup di realm/security manager di qualità di produzione, ma è abbastanza buono per me testarlo. Successivamente, creo le mie classi di manager con un ambito molto limitato che vengono utilizzati dai componenti della mia applicazione. Ho due di questi; a ThriftAuthenticationManager e a ThriftAuthorizationManager. Ecco il primo:

@Singleton 
public class ThriftAuthenticationManager { 
    private final Logger log = LoggerFactory.getLogger(ThriftAuthenticationManager.class); 

    private final SecurityManager securityManager; 

    @Inject 
    public ThriftAuthenticationManager(SecurityManager securityManager) { 
     this.securityManager = securityManager; 
    } 

    public String authenticate(String username, String password) throws TException { 
     try { 
      Subject currentUser = new Subject.Builder(securityManager).buildSubject(); 

      if (!currentUser.isAuthenticated()) { 
       currentUser.login(new UsernamePasswordToken(username, password)); 
      } 

      String authToken = currentUser.getSession().getId().toString(); 
      Preconditions.checkState(!Strings.isNullOrEmpty(authToken)); 
      return authToken; 
     } 
     catch (AuthenticationException e) { 
      throw Exceptions.security(SecurityExceptions.AUTHENTICATION_EXCEPTION); 
     } 
     catch(Throwable t) { 
      log.error("Unexpected error during authentication.", t); 
      throw new TException("Unexpected error during authentication.", t); 
     } 

    } 
} 

E quest'ultimo:

@Singleton 
public class ThriftAuthorizationManager { 
    private final Logger log = LoggerFactory.getLogger(ThriftAuthorizationManager.class); 

    private final SecurityManager securityManager; 

    @Inject 
    public ThriftAuthorizationManager(SecurityManager securityManager) { 
     this.securityManager = securityManager; 
    } 

    public void checkPermissions(final String authToken, final String permissions) 
      throws TException { 
     withThriftExceptions(new Callable<Void>() { 
      @Override 
      public Void call() throws Exception { 
       securityManager.checkPermission(getPrincipals(authToken), permissions); 
       return null; 
      } 
     }); 
    } 

    public void checkPermission(final String authToken, final Permission permission) 
      throws TException { 
     withThriftExceptions(new Callable<Void>() { 
      @Override 
      public Void call() throws Exception { 
       securityManager.checkPermission(getPrincipals(authToken), permission); 
       return null; 
      } 
     }); 
    } 

    private Subject getSubject(String authToken) { 
     return new Subject.Builder(securityManager).sessionId(authToken).buildSubject(); 
    } 

    private PrincipalCollection getPrincipals(String authToken) { 
     return getSubject(authToken).getPrincipals(); 
    } 

    private void withThriftExceptions(Callable<Void> callable) throws TException { 
     try { 
      callable.call(); 
     } 
     catch(SessionException e) { 
      throw Exceptions.security(SecurityExceptions.SESSION_EXCEPTION); 
     } 
     catch(UnauthenticatedException e) { 
      throw Exceptions.security(SecurityExceptions.UNAUTHENTICATED_EXCEPTION); 
     } 
     catch(AuthorizationException e) { 
      throw Exceptions.security(SecurityExceptions.AUTHORIZATION_EXCEPTION); 
     } 
     catch(ShiroException e) { 
      throw Exceptions.security(SecurityExceptions.SECURITY_EXCEPTION); 
     } 
     catch(Throwable t) { 
      log.error("An unexpected error occurred during authorization.", t); 
      throw new TException("Unexpected error during authorization.", t); 
     } 
    } 
} 

miei servizi Thrift utilizzano queste due classi per l'autenticazione e l'autorizzazione. Ad esempio:

@Singleton 
public class EchoServiceImpl implements EchoService.Iface { 
    private final Logger log = LoggerFactory.getLogger(EchoServiceImpl.class); 

    private final ThriftAuthorizationManager authorizor; 

    @Inject 
    public EchoServiceImpl(ThriftAuthorizationManager authorizor) { 
     this.authorizor = authorizor; 
    } 

    @Override 
    public Echo echo(String authToken, Echo echo) throws TException { 
     authorizor.checkPermissions(authToken, "echo"); 
     return echo; 
    } 
} 

Quindi, suppongo di avere effettivamente alcune missioni.

  1. L'errore che ho citato è effettivamente un errore o solo un messaggio di registro troppo zelante?

  2. Devo preoccuparmi che Shiro si affidi a qualsiasi cosa in un ThreadContext se non utilizzo mai ShiroUtils?

  3. C'è qualche danno nell'uso di SecurityUtils#setSecurityManager se non posso garantire un ambiente a thread singolo per richiesta?

  4. Non ho ancora provato a utilizzare le autorizzazioni avanzate di Shiro (org.apache.shiro.authz.Permission). Si affidano a qualsiasi cosa in un ThreadContext o fanno qualcosa di strano che dovrei esaminare prima?

  5. Ho fatto qualcos'altro che potrebbe causarmi problemi o potrei migliorare qualcosa?

risposta

7
  1. L'errore citato è un errore solo se si desidera chiamare SecurityUtils.getSecurityManager(). Sei più che benvenuto per passarlo manualmente o iniettarlo come dipendenza al di fuori dell'uso di SecurityUtils.

  2. SecurityUtils è principalmente una comodità per chi usa Shiro se lo desidera utilizzare. Solo poche cose all'interno di Shiro lo chiamano esplicitamente: alcune delle integrazioni di Shiro in AspectJ lo chiamano, alcune delle soluzioni di Shiro lo chiamano (Servlet Filters, tagli di JSP e JSF). Tuttavia, in questi casi in cui viene utilizzato in Shiro, è (penso) sempre chiamato da un metodo template, che consente di sovrascrivere quel metodo per acquisire il Soggetto da qualche altra parte se lo si desidera.

  3. Nessun danno nella chiamata SecurityUtils.setSecurityManager finché si ha dimestichezza con una singola istanza SecurityManager per l'intera JVM. Se hai più di un'app Shiro che usa quel metodo nella stessa JVM, probabilmente causerebbe problemi. Anche così però, perché le chiamate memoria statica e stato globale sono evil, se si può trovare anche un altro modo di fare riferimento al SecurityManager, che sarebbe ancora meglio (ad esempio Dependency Injection)

  4. permessi Shiro non si basano su niente ThreadContext correlate . I controlli di autorizzazione sono delegati a uno o più Realm s e hanno l'ultima parola su ciò che è permesso o meno. La maggior parte dei reami utilizza a sua volta un'autorizzazione Cache per garantire che le ricerche sui permessi siano gradevoli e reattive. Ma questo non è lo stato del thread: è lo stato singleton dell'applicazione (non statico).

  5. Il tuo codice sembra abbastanza buono. Una raccomandazione per i controlli di autorizzazione ThriftAuthorizationManager consiste nel delegare direttamente all'oggetto (il soggetto a sua volta delega a SecurityManager). Questo credo sia un po 'più veloce rispetto al metodo attuale e migliore auto-documentazione IMO:

    return getSubject(authToken).checkPermission(permission); 
    

Grazie per aver condiviso le vostre domande. È sempre bello vedere gli usi non web del framework, poiché è stato progettato per tutti i carichi di lavoro.

4

Questo è quello che ho scoperto quando ho trovato lo stesso problema: ho aggiunto la dichiarazione del filtro shiro sul mio file web.xml quindi ho chiamato direttamente l'oggetto nel mio bean. ho aggiunto:

<filter> 
     <filter-name>ShiroFilter</filter-name> 
     <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> 
</filter> 
<filter-mapping> 
     <filter-name>ShiroFilter</filter-name> 
     <url-pattern>/*</url-pattern> 
     <dispatcher>REQUEST</dispatcher> 
     <dispatcher>FORWARD</dispatcher> 
     <dispatcher>INCLUDE</dispatcher> 
     <dispatcher>ERROR</dispatcher> 
</filter-mapping> 

e poi, dopo che sono andato in scrittura solo la seguente dichiarazione:

Subject subject = SecurityUtils.getSubject(); 
Problemi correlati