Short version:
I have setup a small maven web project (JDK 1.8.0, Source: 1.8, JavaEE7) to test the JDBCRealm in Glassfish 4.1 (build 13). It consists of two Entities: RealmUser und RealmGroup and a JAX-RS Service to insert, update and delete these Entities.
I defined the JDBCResource in glassfish-resources.xml, configured the realm via Glassfish Admin Console, and setup web.xml with login-config (BASIC) only.
When using a WebFilter to activate authentication it works fine.
But when i setup a security constraint in the web.xml instead, i get the following exception whenever i try to access the protected resource (Full Stacktrace and details below):
Schwerwiegend: jdbcrealm.invaliduser
Fine: Cannot validate user
javax.security.auth.login.LoginException: Unable to connect to datasource java:app/jdbc/sampleresource for database user null.
...
Caused by: javax.naming.NamingException: Lookup failed for 'java:app/jdbc/sampleresource' in SerialContext[myEnv={com.sun.enterprise.connectors.jndisuffix=__nontx, java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NamingException: Invocation exception: Got null ComponentInvocation ]
...
Caused by: javax.naming.NamingException: Invocation exception: Got null ComponentInvocation
...
Your help is greatly appreciated!
Step by Step:
I created a Derby Database (SampleDB, user: testuser, password: pw) and defined a JDBC resource via glassfish-resources.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-resource enabled="true" jndi-name="java:app/jdbc/sampleresource" object-type="user" pool-name="samplepool">
<description/>
</jdbc-resource>
<jdbc-connection-pool datasource-classname="org.apache.derby.jdbc.ClientDataSource40" name="samplepool" res-type="javax.sql.DataSource">
<property name="URL" value="jdbc:derby://localhost:1527/SampleDB"/>
<property name="serverName" value="localhost"/>
<property name="portNumber" value="1527"/>
<property name="databaseName" value="SampleDB"/>
<property name="User" value="testuser"/>
<property name="Password" value="pw"/>
<property name="driverClass" value="org.apache.derby.jdbc.ClientDriver"/>
</jdbc-connection-pool>
</resources>
I created the persistence.xml with jta-data-source set to java:app/jdbc/sampleresource:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="SampleRealmPU" transaction-type="JTA">
<jta-data-source>java:app/jdbc/sampleresource</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
</properties>
</persistence-unit>
</persistence>
On Startup the Application adds a default user (admin, pw) and default group (Administrator), and assigns admin to the Administrator group.
Then i created the JDBC Realm using the Admin Console, and pointed the Realm to java:app/jdbc/sampleresource.
Note:
- I added Database username and password entries although they should be redundant.
- Default Principal To Role Mapping is Enabled.
I implemented two services:
- GET on .../webresources/UserService/users returns the list of all users
- GET on .../webresources/UserService/groups return the list of all groups
Now to activate authentication for accessing groups i created a very simple web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>SampleRealm</realm-name>
</login-config>
</web-app>
and a WebFilter:
@WebFilter("/webresources/UserService/groups/*")
public class Authenticator implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (req.authenticate(resp)) {
chain.doFilter(req, resp);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override
public void destroy() {
}
}
So far it works! I have to provide my default user credentials to access the groups.
Now instead of using the Webfilter i want to use the web.xml to define the same authentication constraint on groups. I commented the whole WebFilter and modified the web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<security-constraint>
<display-name>GroupConstraint</display-name>
<web-resource-collection>
<web-resource-name>UserService groups</web-resource-name>
<description/>
<url-pattern>/webresources/UserService/groups/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>requires auth</description>
<role-name>Administrator</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>SampleRealm</realm-name>
</login-config>
<security-role>
<description/>
<role-name>Administrator</role-name>
</security-role>
</web-app>
With this setup i can access the users resource (i.e. database is there) but when i try to access the groups resource i get HTTP Status 401 - Unauthorized and Glassfish logs:
Fine: [Web-Security] Setting Policy Context ID: old = null ctxID = samples-realm-web/samples-realm-web
Fine: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/webresources/UserService/groups" "GET")
Fine: [Web-Security] hasUserDataPermission isGranted: true
Fine: [Web-Security] Policy Context ID was: samples-realm-web/samples-realm-web
Fine: [Web-Security] Generating a protection domain for Permission check.
Fine: [Web-Security] Codesource with Web URL: file:/samples-realm-web/samples-realm-web
Fine: [Web-Security] Checking Web Permission with Principals : null
Fine: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/webresources/UserService/groups" "GET")
Finest: JACC Policy Provider: PolicyWrapper.implies, context (samples-realm-web/samples-realm-web)- result was(false) permission (("javax.security.jacc.WebResourcePermission" "/webresources/UserService/groups" "GET"))
Fine: [Web-Security] hasResource isGranted: false
Fine: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/UserService/groups" "GET")
Finest: Processing login with credentials of type: class com.sun.enterprise.security.auth.login.common.PasswordCredential
Fine: Logging in user [admin] into realm: SampleRealm using JAAS module: jdbcRealm
Fine: Login module initialized: class com.sun.enterprise.security.ee.auth.login.JDBCLoginModule
Schwerwiegend: jdbcrealm.invaliduser
Fine: Cannot validate user
javax.security.auth.login.LoginException: Unable to connect to datasource java:app/jdbc/sampleresource for database user null.
at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.getConnection(JDBCRealm.java:585)
at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.isUserValid(JDBCRealm.java:408)
at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.authenticate(JDBCRealm.java:324)
at com.sun.enterprise.security.ee.auth.login.JDBCLoginModule.authenticate(JDBCLoginModule.java:78)
at com.sun.enterprise.security.auth.login.PasswordLoginModule.authenticateUser(PasswordLoginModule.java:116)
at com.sun.enterprise.security.BasePasswordLoginModule.login(BasePasswordLoginModule.java:145)
at sun.reflect.GeneratedMethodAccessor52.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:755)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:195)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:682)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:680)
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:587)
at com.sun.enterprise.security.auth.login.LoginContextDriver.doPasswordLogin(LoginContextDriver.java:383)
at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:241)
at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:154)
at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:695)
at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:636)
at org.apache.catalina.authenticator.BasicAuthenticator.authenticate(BasicAuthenticator.java:166)
at com.sun.web.security.RealmAdapter.invokeAuthenticateDelegate(RealmAdapter.java:1524)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:606)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:702)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:415)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:282)
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:201)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:175)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.naming.NamingException: Lookup failed for 'java:app/jdbc/sampleresource' in SerialContext[myEnv={com.sun.enterprise.connectors.jndisuffix=__nontx, java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NamingException: Invocation exception: Got null ComponentInvocation ]
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:491)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:438)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
at org.glassfish.resourcebase.resources.naming.ResourceNamingService.lookup(ResourceNamingService.java:236)
at com.sun.enterprise.connectors.service.ConnectorResourceAdminServiceImpl.lookup(ConnectorResourceAdminServiceImpl.java:224)
at com.sun.enterprise.connectors.ConnectorRuntime.lookupNonTxResource(ConnectorRuntime.java:553)
at com.sun.enterprise.connectors.ConnectorRuntime.lookupNonTxResource(ConnectorRuntime.java:538)
at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.getConnection(JDBCRealm.java:573)
... 48 more
Caused by: javax.naming.NamingException: Invocation exception: Got null ComponentInvocation
at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.getComponentId(GlassfishNamingManagerImpl.java:842)
at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:714)
at com.sun.enterprise.naming.impl.JavaURLContext.lookup(JavaURLContext.java:167)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:471)
... 56 more
Fine: JAAS authentication aborted.
Finest: doPasswordLogin fails
javax.security.auth.login.LoginException: Security Exception
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:840)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:195)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:682)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:680)
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:587)
at com.sun.enterprise.security.auth.login.LoginContextDriver.doPasswordLogin(LoginContextDriver.java:383)
at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:241)
at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:154)
at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:695)
at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:636)
at org.apache.catalina.authenticator.BasicAuthenticator.authenticate(BasicAuthenticator.java:166)
at com.sun.web.security.RealmAdapter.invokeAuthenticateDelegate(RealmAdapter.java:1524)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:606)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:702)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:415)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:282)
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:201)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:175)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.SecurityException
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:841)
... 39 more
Warnung: WEB9102: Web Login Failed: com.sun.enterprise.security.auth.login.common.LoginException: Login failed: Security Exception
I tried several things to solve this including:
- renaming the jdbc resource
- leaving/adding the java:app namespace at several points
- creating a completly new project to test
- added Role Mappings (despite having default Role mapping enabled)
Example of RoleMappings using glassfish-web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMSSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/h2ee web-app_2_4.xsd">
<security-role-mapping>
<role-name>Administrator</role-name>
<group-name>Administrator</group-name>
</security-role-mapping>
</glassfish-web-app>
Your help is greatly appreciated!
your password Encryption is set to SHA-256... Make it AES
USE SHA-256 as the digest Algo not the encryption... try using Hex as the Encoding