6

Tomcat 7.0.14 で Web アプリケーションを実行しており、ユーザー認証に LDAP を使用しています。問題は、ユーザーが非アクティブ期間の後にログインすると、次の警告が表示されることです。非アクティブ期間は長くする必要はありません。数分あれば十分です。ただし、ユーザーは警告にもかかわらずログインできます。ユーザーの観点から見ると、アプリケーションは正常に動作しますが、Tomcat ログには以下の警告が表示されます。

Jun 6, 2012 9:41:19 AM org.apache.catalina.realm.JNDIRealm authenticate  
WARNING: Exception performing authentication  
javax.naming.CommunicationException [Root exception is java.io.IOException: connection closed]; remaining name ''  
        at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:157)  
        at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2685)  
        at com.sun.jndi.ldap.LdapCtx.ensureOpen(LdapCtx.java:2593)  
        at com.sun.jndi.ldap.LdapCtx.ensureOpen(LdapCtx.java:2567)  
        at com.sun.jndi.ldap.LdapCtx.doSearch(LdapCtx.java:1932)  
        at com.sun.jndi.ldap.LdapCtx.doSearchOnce(LdapCtx.java:1924)  
        at com.sun.jndi.ldap.LdapCtx.c_getAttributes(LdapCtx.java:1317)  
        at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:231)  
        at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:139)  
        at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:127)  
        at javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:140)  
        at org.apache.catalina.realm.JNDIRealm.bindAsUser(JNDIRealm.java:1621)  
        at org.apache.catalina.realm.JNDIRealm.checkCredentials(JNDIRealm.java:1480)  
        at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:1131)  
        at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:1016)  
        at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:282)  
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:440)  
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)  
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)  
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)  
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)  
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:399)  
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:317)  
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:204)  
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:311)  
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)  
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)  
        at java.lang.Thread.run(Thread.java:636)  
Caused by: java.io.IOException: connection closed  
        at com.sun.jndi.ldap.LdapClient.ensureOpen(LdapClient.java:1576)  
        at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:155)  
        ... 27 more  

LDAP 構成は、アプリケーションの context.xml ファイルにあります。

<Realm className="org.apache.catalina.realm.JNDIRealm"  
    connectionURL="ldaps://ldap-company.com"  
    userPattern="uid={0},dc=company,dc=com"  
    roleBase="ou=groups,o=company"  
    roleName="uid"  
    roleSearch="uniqueMember={0}"  
    roleSubtree="true" />  

いくつかのフォーラムからこの問題に関する投稿を見つけましたが、誰も解決策を見つけていないようです。

4

4 に答える 4

6

警告の理由とそれを取り除く方法を理解することができました。

警告の理由は、LDAP サーバーが 5 分以上アイドル状態だったすべての接続を閉じているためです。LDAP サーバーの管理者は、使用可能なハンドルの数が限られているため、各ログイン要求の直後に接続を閉じることをお勧めすると教えてくれました。ただし、Tomcat の JNDIRealm はこれを構成する方法を提供していないため、JNDIRealm クラスを拡張し、authenticate(..) メソッドをオーバーライドすることで問題を解決しました。各認証要求と警告が消えた後、LDAP サーバーへの接続を閉じるだけで済みます。

パッケージは JNDIRealm クラスと同じである必要があることに注意してください。そうしないと、コンテキスト変数にアクセスできなくなります。

package org.apache.catalina.realm;

import java.security.Principal;

public class CustomJNDIRealm extends JNDIRealm {
  @Override
  public Principal authenticate(String username, String credentials) {
  Principal principal = super.authenticate(username, credentials);

    if (context != null) {
      close(context);
    }
    return principal;
  }
}

生成された jar は、Tomcat の lib フォルダーの下に配置し、アプリケーションの context.xml の className を org.apache.catalina.realm.CustomJNDIRealm に変更する必要があります。次に、Tomcat を再起動するだけです。

<Realm className="org.apache.catalina.realm.CustomJNDIRealm"  
  connectionURL="ldaps://ldap-company.com"  
  userPattern="uid={0},dc=company,dc=com"  
  roleBase="ou=groups,o=company"  
  roleName="uid"  
  roleSearch="uniqueMember={0}"  
  roleSubtree="true" /> 
于 2012-06-07T07:32:57.863 に答える
3

私は現在、ニーズに合わせて JNDIRealm を拡張しているため、これは私にとって現在の研究トピックであるため、回答しています。

レルムは警告後に再試行するため、提案されたパッチはログファイルを美しくするだけです。Tomcat の新しいバージョン (7.0.45 iirc) では、ログメッセージが美しくなり、再試行が行われたことが明確になります。

レルムが毎回新しい接続で認証を行うようにしたい場合は、このクラスを使用するだけで十分です (この実装はテストしていませんが、レルムが完了すればテストします)。

package org.apache.catalina.realm;

import java.security.Principal;

public class CustomJNDIRealm extends JNDIRealm {
  @Override
  public Principal authenticate(String username, String credentials) {
    Principal principal = null;
    DirContext context = null;
    try {
       context = open();
       principal = super.authenticate(context, username, credentials);
    }
    catch(Throwable t) {
       // handle errors
       principal = null;
    }
    finally {
       close(context); // JNDIRealm close() takes care of null context
    }

    return principal;
  }

  @Override
  protected DirContext open() throws NamingException {

      // do no longer use the instance variable for context caching
      DirContext context = null;

      try {

          // Ensure that we have a directory context available
          context = new InitialDirContext(getDirectoryContextEnvironment());

      } catch (Exception e) {

          connectionAttempt = 1;

          // log the first exception.
          containerLog.warn(sm.getString("jndiRealm.exception"), e);

          // Try connecting to the alternate url.
          context = new InitialDirContext(getDirectoryContextEnvironment());

      } finally {

          // reset it in case the connection times out.
          // the primary may come back.
          connectionAttempt = 0;

      }

      return (context);

  }


}
于 2014-11-13T10:08:02.813 に答える
1

LDAP サーバーは、一定の時間が経過した後、アイドル状態 (つまり、要求が送信されていない) のアイドル接続を切断しています。

于 2012-06-06T11:27:44.853 に答える