2013-05-03 7 views
7

Ich füge Apache Shiro meine Anwendung und ich frage mich, wenn die folgende Fehlermeldung wirklich korrekt ist:Ist ein nicht gebundener SecurityManager wirklich eine ungültige Anwendungskonfiguration in Shiro?

org.apache.shiro.UnavailableSecurityManagerException: Nein Security zugänglich für die Telefonvorwahl, entweder gebunden an org.apache.shiro.util.ThreadContext oder als statischer vm-Singleton. Dies ist eine ungültige Anwendungskonfiguration.

Ich habe ein bisschen und der Eindruck durch den Quellcode sehe, dass ich ist erhalten, dass, solange ich nicht SecurityUtils mit und ich bin bereit, eine SecurityManager zu Komponenten zu übergeben, die es, ich don brauchen muss den SecurityManager nicht dem statischen Singleton zuweisen, der von SecurityUtils verwendet wird.

Die spezifische Sache, die ich vermeiden möchte, ist Shiro legte alles in ThreadLocal oder mit Shiro verwenden Sie seine ThreadContext Support-Klasse. Ich benutze Apache Thrift und möchte mich nicht für ein One-Thread-pro-Request-Netzwerkdesign einsetzen. Meine Anforderungen von Shiro sind ziemlich minimal, also werde ich zeigen, was ich unten mache.

Ich verwende Guice in meiner Anwendung, aber ich bin nicht mit shiro-guice weil die Shiro AOP Sachen ein Subject auf, die in Verbindung mit einem ThreadContext abhängen. Stattdessen beginne ich mit einem sehr einfachen Guice-Modul.

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

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

Das ist nicht gerade eine Produktionsqualität Bereich/Security Manager-Setup, aber es ist gut genug für mich mit zu testen. Als nächstes erstelle ich eigene Manager-Klassen mit einem sehr begrenzten Umfang, die von den Komponenten meiner Anwendung verwendet werden. Ich habe zwei davon; a ThriftAuthenticationManager und ThriftAuthorizationManager. Hier ist die ehemalige:

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

    } 
} 

Und dieser:

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

Meine Thrift Dienste verwenden, um die beiden oben genannten Klassen für die Authentifizierung und Autorisierung. Zum Beispiel:

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

Also, ich denke, ich habe tatsächlich ein paar Quests.

  1. Ist der Fehler, den ich zitiert habe, tatsächlich ein Fehler oder nur eine übereifrige Protokollnachricht?

  2. Muss ich mir Sorgen machen, dass sich Shiro auf irgendetwas in einem ThreadContext verlässt, wenn ich nie ShiroUtils verwende?

  3. Gibt es einen Schaden bei der Verwendung von SecurityUtils#setSecurityManager, wenn ich keine One-Thread-pro-Request-Umgebung garantieren kann?

  4. Ich habe noch nicht die erweiterten Berechtigungen von Shiro (org.apache.shiro.authz.Permission) versucht. Verlassen sie sich auf irgendetwas in einem ThreadContext oder machen Sie etwas Unheimliches, das ich früher oder später untersuchen sollte?

  5. Habe ich irgendetwas anderes gemacht, das mir Probleme bereiten könnte oder könnte ich etwas verbessern?

Antwort

7
  1. Der Fehler Angabe ist ein Fehler nur, wenn Sie SecurityUtils.getSecurityManager() anrufen möchten. Sie können es gerne manuell weitergeben oder es als eine Abhängigkeit außerhalb der SecurityUtils-Nutzung injizieren.

  2. SecurityUtils ist vor allem eine Bequemlichkeit für diejenigen, die Shiro verwenden, wenn sie es verwenden möchten. Nur ein paar Dinge in Shiro nennen es tatsächlich explizit: einige von Shiros AspectJ-Integration nennen es, einige von Shiros Web-Support nennen es (Servlet-Filter, JSP- und JSF-Taglibs). In diesen Fällen, in denen es innerhalb von Shiro verwendet wird, wird es (wie ich meine) immer mit einer Template-Methode aufgerufen, so dass Sie diese Methode überschreiben können, um das Subject von woanders zu beziehen, falls dies gewünscht wird.

  3. Kein Problem beim Aufruf SecurityUtils.setSecurityManager, solange Sie mit einer einzigen SecurityManager-Instanz für Ihre gesamte JVM vertraut sind. Wenn Sie mehr als eine Shiro-App verwenden, die diese Methode in derselben JVM verwendet, würde dies wahrscheinlich zu Problemen führen. Trotzdem aber, weil statische Speicher Anrufe und globalen Zustand evil sind, auch wenn Sie eine andere Art der Referenzierung der Securitymanager finden, das wäre noch besser (zB Dependency Injection)

  4. Shiro Berechtigungen verlassen sich nicht auf irgendetwas ThreadContext verwandten . Berechtigungsüberprüfungen werden an einen oder mehrere Realm s delegiert, und sie haben das letzte Wort darüber, was erlaubt ist oder nicht. Die meisten Realms wiederum verwenden eine Autorisierung Cache, um zu gewährleisten, dass Berechtigungsnachfragen nett und reaktionsfähig bleiben. Aber dies ist kein Thread-Status - es ist (nicht statische) Anwendung Singleton-Zustand.

  5. Ihr Code sieht ziemlich gut aus. Eine Empfehlung für die Berechtigungsprüfungen ThriftAuthorizationManager besteht darin, direkt an den Subjekt zu delegieren (das Subjekt wiederum delegiert an den SecurityManager). Dies ist meiner Meinung nach ein bisschen schneller als der aktuelle Ansatz und besser selbsterklärend IMO:

    return getSubject(authToken).checkPermission(permission); 
    

Vielen Dank für Ihre Fragen zu teilen. Es ist immer gut zu sehen, dass das Framework nicht im Web verwendet wird, da es für alle Workloads entwickelt wurde.

4

Dies ist, was ich entdeckte, als ich auf das gleiche Problem stieß: Ich fügte die Shiro-Filter-Anweisung in meine web.xml-Datei dann direkt als Betreff in meiner Bean. Ich fügte hinzu:

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

und dann nach, dass ich beim Schreiben ging gerade die folgende Erklärung ab:

Subject subject = SecurityUtils.getSubject();