У меня есть веб-приложение, в котором объявлена стандартная проверка подлинности, например:
<login-config>
<auth-method>FORM</auth-method>
<realm-name>ldap-realm</realm-name>
<form-login-config>
<form-login-page>/home</form-login-page>
<form-error-page>/home?error=true</form-error-page>
</form-login-config>
</login-config>
У меня есть ldap-realm, объявленный на моем сервере Glassfish, со всеми необходимыми учетными данными для подключения к моему LDAP-серверу.
Проблема заключается в следующем:
В Glassfish V2, если я разверну это приложение, при попытке войти в систему с неверным пользователем (или существующим пользователем, но с неверным паролем) меня перенаправят на URL-адрес, объявленный в <form-error-page>
(/ home? Error = true), и на эту страницу отображает удобное сообщение об ошибке, такое как «Вы не смогли аутентифицировать бла бла». Кроме того, если в этот момент я просматриваю журналы, то вижу сообщение ПРЕДУПРЕЖДЕНИЕ о том, что введенные пользователем учетные данные не могут быть аутентифицированы на сервере LDAP.
На Glassfish V3 (пробовал как на 3.0, так и на 3.1), когда я развертываю одно и то же приложение и у меня точно такая же конфигурация ldap, настроенная в GF, когда пользователь пытается пройти аутентификацию с использованием ПРАВИЛЬНЫХ учетных данных, все идет нормально, но когда он вводит плохого пользователя или хорошего пользователя / неверный пароль Я получаю сообщение об ошибке 404 страница не найдена вместо перенаправления на URL с <form-error-page>
. Более того, случается так, что после неверной аутентификации пользователь видит страницу HTML, которую я объявил для 404 ошибок, вместо страницы формы ошибки. Также в журнале Glassfish у меня теперь есть трассировка стека исключений, подобная этой:
[#|2012-01-05T16:49:28.878+0100|WARNING|glassfish3.1|javax.enterprise.system.container.web.com.sun.web.security|_ThreadID=130;_ThreadName=Thread-1;|Exception
com.sun.enterprise.security.auth.login.common.LoginException: Login failed: Failed file login for aaaa.
at com.sun.enterprise.security.auth.login.LoginContextDriver.doPasswordLogin(LoginContextDriver.java:394)
at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:240)
at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:153)
at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:483)
at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:425)
at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:269)
at org.apache.catalina.authenticator.AuthenticatorBase.processSecurityCheck(AuthenticatorBase.java:909)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:546)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:623)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:326)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:227)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:170)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:822)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:719)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1013)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:662)
Caused by: javax.security.auth.login.LoginException: Failed file login for aaaa.
at com.sun.enterprise.security.auth.login.FileLoginModule.authenticate(FileLoginModule.java:84)
at com.sun.enterprise.security.auth.login.PasswordLoginModule.authenticateUser(PasswordLoginModule.java:117)
at com.sun.appserv.security.AppservPasswordLoginModule.login(AppservPasswordLoginModule.java:148)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:769)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680)
at javax.security.auth.login.LoginContext.login(LoginContext.java:579)
at com.sun.enterprise.security.auth.login.LoginContextDriver.doPasswordLogin(LoginContextDriver.java:382)
Честно говоря, я довольно озадачен. Область ldap определена правильно, поскольку VALID-аутентификация работает нормально. Также допустим URL-адрес страницы формы-ошибки, потому что, если я введу его вручную в адресную строку, я получу страницу ошибки аутентификации. Единственное, о чем я думаю, - это создать собственную реализацию LdapRealm и больше не генерировать это исключение при сбое аутентификации. Но это не очень круто.
Мой вопрос: кто-нибудь знает, есть ли разница в том, как Glassfish V2 и V3 обрабатывают неудачные аутентификации? Есть ли дополнительный параметр конфигурации, доступный для Glassfish V3, который заставит его возвращать страницу ошибки формы при неверной аутентификации пользователя вместо того, чтобы выдавать исключение и возвращать 404?