Glassfish 5.1 上带有身份验证表单的空指针异常

null pointer exception with authentication form on Glassfish 5.1

根据官方文档,我有一个非常简单的 JEE 登录表单,它在本地 Glassfish 5.1 开发机器上运行良好。

https://docs.oracle.com/javaee/6/tutorial/doc/gkbaa.html

但是,当部署在集群实例中的生产环境中时,相同的 5.1 版本,当点击任何需要身份验证的页面时,我得到以下堆栈跟踪。 该领域是一个常规 "file" 领域。

  Unexpected error forwarding or redirecting to login page
java.lang.NullPointerException
    at com.sun.logging.LogCleanerUtil.neutralizeForLog(LogCleanerUtil.java:70)
    at org.apache.catalina.core.ApplicationDispatcher.<init>(ApplicationDispatcher.java:197)
    at org.apache.catalina.core.StandardContext.getRequestDispatcher(StandardContext.java:7382)
    at org.apache.catalina.core.ApplicationContext.getRequestDispatcher(ApplicationContext.java:329)
    at org.apache.catalina.core.ApplicationContextFacade.getRequestDispatcher(ApplicationContextFacade.java:249)
    at org.apache.catalina.authenticator.FormAuthenticator.forwardToLoginPage(FormAuthenticator.java:473)
    at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:222)
    at com.sun.web.security.RealmAdapter.invokeAuthenticateDelegate(RealmAdapter.java:1515)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:536)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:579)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:550)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:75)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:114)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:332)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:199)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:439)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:144)
    at org.glassfish.grizzly.http.server.HttpHandler.run(HttpHandler.java:200)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
    at java.lang.Thread.run(Thread.java:748)
]]

我查看了LogCleanerUtilclass,应该是开源的

public static String neutralizeForLog(String message){
    StringBuilder sb = new StringBuilder();
    for(int offset  = 0; offset < message.length(); ){
        final int point = message.codePointAt(offset);
        if(Character.isValidCodePoint(point)){
            sb.append(encodeCharacter(point));
        }
        offset += Character.charCount(point);
    }
    return sb.toString();
}

由于 message 为空,因此出现空指针。 问题是为什么 message 是 null ?我们是在谈论日志消息吗?除了 OS 版本之外,与本地环境的唯一区别是在生产服务器上我使用 asadmin 命令创建了 glassfish 领域和整个集群。

这里是web.xml

<security-constraint>
    <display-name>Admin Constraint</display-name>
    <web-resource-collection>
        <web-resource-name>Admin</web-resource-name>
        <url-pattern>/console/*</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>
<security-role>
    <description>Console Admin</description>
    <role-name>admin</role-name>
</security-role>
<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>console-realm</realm-name>
    <form-login-config>
        <form-login-page>/login.xhtml</form-login-page>
        <form-error-page>/loginError.xhtml</form-error-page>
    </form-login-config>
</login-config>

好吧...经过一些调查后发现至少对我来说它似乎是一个错误。我已经在 glassfish 存储库中打开了一个 issue

根据堆栈跟踪,StandardContext.java 在第 7382 行传递的 name 为空

    // Construct a RequestDispatcher to process this request
    return new ApplicationDispatcher
        (wrapper, mappingForDispatch, uriCC.toString(), wrapperPath, pathInfo,
         queryString, null);

因此,调用这些方法会导致空指针异常,因为如果您查看 ApplicationDispatcher.java

,生产中的日志级别为 FINE(因为尚未生产)
    if (log.isLoggable(Level.FINE))
        log.log(Level.FINE, "servletPath= " + neutralizeForLog(this.servletPath) + ", pathInfo= "
                + neutralizeForLog(this.pathInfo) + ", queryString= " + neutralizeForLog(queryString) + ", name= "
                + neutralizeForLog(this.name));

最终 neutralizeForLog 失败了,因为消息实际上是空的。