2013-05-03 4 views
7

は、私は自分のアプリケーションにはApache史郎を追加していると、次のエラーメッセージが本当に正確である場合、私は思ったんだけど:封印されていないSecurityManagerは実際にShiroの無効なアプリケーション構成ですか?

org.apache.shiro.UnavailableSecurityManagerException:いいえSecurityManagerを呼び出しコードにアクセス可能な、のいずれかに結合しorg.apache.shiro.util.ThreadContextまたはvm静的シングルトンとして宣言します。これは無効なアプリケーション構成です。私は、ソースコードを介してビットを見てきたし、私が得る印象がある限り、私はSecurityUtilsを使用していないとということであり、私がそれを必要とするコンポーネントにSecurityManagerを渡すために喜んだ、私ドン

SecurityUtilsが使用するスタティックシングルトンに実際にSecurityManagerを割り当てる必要があります。

私が避けたいのは、シロはThreadLocalに何かを入れているか、シロはそのThreadContextサポートクラスを使用していることです。私はApache Thriftを使用しており、要求ごとに1スレッドのネットワーク設計に専念したくありません。城からの私の要求はかなり少ないので、私が下でやっていることを見せてくれるでしょう。

私は自分のアプリケーションでGuiceのを使用していますが、史郎AOPのものがThreadContextに関連付けられているSubjectを持つに依存しているため、私はshiro-guiceを使用してないよ。代わりに、私は非常に簡単なGuiceモジュールから始めます。

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

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

これは、本物のレルム/セキュリティマネージャの設定ではありませんが、テストするには十分です。次に、自分のアプリケーションのコンポーネントで使用される非常に限られたスコープを持つ独自のマネージャークラスを作成します。私はこれらの2つを持っています。 ThriftAuthenticationManagerおよびThriftAuthorizationManagerである。ここでは前者は次のとおりです。

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

    } 
} 

、後者:

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

マイスリフトサービスは、認証と承認のために上記の2つのクラスを使用します。例:

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

だから、私は実際にいくつかの欲求を持っていると思います。

  1. 引用したエラーは実際にはエラーですか、過度のログメッセージですか?

  2. ShiroUtilsを絶対に使用しないと、シロは、ThreadContextにあるものに頼っているのではないですか?

  3. 1スレッドあたりのリクエスト環境を保証できない場合は、SecurityUtils#setSecurityManagerを使用することに何らかの害がありますか?

  4. 私はShiroの高度なアクセス許可(org.apache.shiro.authz.Permission)をまだ試していません。彼らはThreadContextの中の何かに頼っているのですか、私は後で早く調べなければならない奇妙なことをしていますか?

  5. 問題が発生する可能性がありますか、何か改善する可能性がありますか?

答えて

7
  1. 引用されたエラーは、SecurityUtils.getSecurityManager()を呼び出したい場合にのみエラーです。手動でSecurityUtilsを使用する以外の方法で依存関係を渡すことは大歓迎です。

  2. SecurityUtilsは、Shiroを使用しているユーザーにとって便利なものです。 ShiroのAspectJ統合の中には、ShiroのWebサポートの中には、Servlet Filters、JSP、JSF taglibというものがあります。しかし、Shiro内で使用されるこれらのケースでは、常にテンプレートメソッドによって呼び出される(私は思う)。もしあなたが望むなら、そのメソッドをオーバーライドして他の場所からSubjectを取得できるようにする。

  3. SecurityUtils.setSecurityManagerあなたのJVM全体に対して1つのSecurityManagerインスタンスがあれば問題ありません。同じJVMでそのメソッドを使用するShiroアプリケーションが複数ある場合は、問題が発生する可能性があります。そうであっても、スタティックメモリコールとグローバル状態がevilているので、あなたがのSecurityManagerを参照するのも、別の方法を見つけることができる場合は、、それはさらに良いでしょう(たとえば、依存性注入)

    史郎の権限は、関連何でもThreadContextに頼らない

  4. 。許可チェックは、1つまたは複数のRealmに委任され、許可されているかどうかに関する最終的な意見があります。ほとんどの領域では、許可の検索を確実に行うためにAuthority Cacheを使用しています。しかし、これはスレッド状態ではなく、(非静的な)アプリケーションシングルトン状態です。

  5. コードはかなりよく見えます。 ThriftAuthorizationManager認可チェックの1つの推奨事項は、サブジェクトに直接委任することです(サブジェクトはSecurityManagerに委任されます)。私が思うこれは、現在のアプローチとIMOよりよい自己文書よりも少し速いです:ご質問を共有するための

    return getSubject(authToken).checkPermission(permission); 
    

感謝。フレームワークのWeb以外の使用法は、すべての作業負荷に合わせて設計されているので、常に参考にしてください。

4

これは私が同じ問題に遭遇したときに発見したものです。私はshiroフィルタステートメントをweb.xmlファイルに追加して、BeanのSubjectを直接呼び出しました。 私が追加:

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

をし、その後、私は書き込みにちょうど次のステートメントを行ってきました:

Subject subject = SecurityUtils.getSubject(); 
関連する問題