2012-06-06 193 views
4

我有一个在Tomcat 7.0.14上运行的Web应用程序,我使用LDAP进行用户身份验证。问题是当用户在非活动时间段之后登录时,会出现以下警告。不活动的时间不一定很长,因为只有几分钟就够了。但是,尽管有警告,用户仍可以登录。从用户的角度来看应用程序运行正常,但Tomcat日志显示下面的警告。Tomcat 7.0.14 LDAP身份验证

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

我发现这个问题的帖子从几个论坛,但似乎没有人想通了解决方案。

+0

无论出于何种原因,tomcat与LDAP服务器的连接可能被LDAP服务器关闭。专业品质的LDAP服务器可能因以下原因而关闭连接:1)不活动2)操作太多3)连接时间过长或其他原因。请与LDAP服务器管理员检查其关于断开退出连接的策略。 –

+0

感谢您的提示!我联系了LDAP服务器管理员,空闲连接的超时时间为3分钟,这说明了警告。现在我应该找到一种方法来摆脱它。 –

回答

1

LDAP服务器正在断开闲置的空闲连接,即在一段时间后没有传输请求。

5

我能弄清楚警告的原因以及摆脱它的一种方法。

警告的原因是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文件夹被投入和改变的className在应用程序的context.xml中来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" /> 
0

基本上添加keepaliveTimeout覆盖连接超时这是约5分钟解决的问题在我的情况,即keepaliveTimeout ="-1"属性在server.xml文件

keepAliveTimeout="-1" 
2

我回答到连接器元件,因为这是一个目前的研究课题,因为我们目前为我们的需求扩展JNDIRalm。

该领域将在警告后重试,所以建议的补丁只是美化日志文件。更高版本的tomcat(7.0.45 iirc)将会美化logmessage以清楚地表明重试尝试完成。

如果你想每次都做认证通过一个新的连接的境界,应该是足够使用这个类(我没有测试这种实现但会在我们的领域做):

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

    } 


}