24

I want to understand form based security and JDBC realms with glassfishV3, so i decided to create a little app that just allows to sign in and out, i followed the instructions from this book to do so.

I understand how the hold thing works, but something is wrong and i cant get it to work properly.

What i did first was create a little database with JPA annotations:

@Entity
@Table(name="USERS")
public class User implements Serializable {

    private static final long serialVersionUID = -1244856316278032177L;
    @Id
    @GeneratedValue
    @Column(nullable = false)
    private Long id;
    @Column(nullable = false)
    private String email;
    @Column(nullable = false)
    private String password;
    @OneToMany(mappedBy = "user")
    private List<Group> groups;
    //GET & SET METHODS...

}

Here the other table that holds the roles for each user

@Entity
@Table(name="GROUPS")
public class Group implements Serializable {

    private static final long serialVersionUID = -7274308564659753174L;
    @Id
    @GeneratedValue
    @Column(nullable = false)
    private Long id;
    @Column(nullable = false)
    private String groupName;
    @ManyToOne
    @JoinColumn(name = "USERS_ID", nullable = false)
    private User user;
    //GET & SET METHODS...
    }

When the DB was ready i added some data manually

enter image description here

The next step was to configure a security realm.

enter image description here

Then added the security configuration to my web.xml file

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <display-name>CHAPTER x 12 Container Managed Authentication and
        Authorization</display-name>
    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>VISITOR PERMISIONS</web-resource-name>           
            <url-pattern>/index.xhtml</url-pattern>         
            <url-pattern>/visitorpanel.xhtml</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>visitors</role-name>
            <role-name>users</role-name>
            <role-name>administrators</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>USERS PERMISIONS</web-resource-name>         
            <url-pattern>/userpanel.xhtml</url-pattern>
            <url-pattern>/index.xhtml</url-pattern>         
            <url-pattern>/visitorpanel.xhtml</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>         
        </web-resource-collection>
        <auth-constraint>           
            <role-name>users</role-name>
            <role-name>administrators</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>ADMIN PERMISIONS</web-resource-name>             
            <url-pattern>/adminpanel.xhtml</url-pattern>
            <url-pattern>/userpanel.xhtml</url-pattern>
            <url-pattern>/index.xhtml</url-pattern>         
            <url-pattern>/visitorpanel.xhtml</url-pattern>          
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>           
            <role-name>administrators</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>DBRealm</realm-name>
        <form-login-config>
            <form-login-page>/index.xhtml</form-login-page>
            <form-error-page>/error.xhtml</form-error-page>
        </form-login-config>
    </login-config>

    <security-role>
    <role-name>visitors</role-name>
    </security-role>

    <security-role>
    <role-name>users</role-name>
    </security-role>

    <security-role>
    <role-name>administrators</role-name>
    </security-role>
</web-app>

My objectives here were:

  • administrators can see all pages

  • visitors can see only index.xhtml and visitorpanel.xhtml

  • users can see index.xhtml,visitorpanel.xhtml and userpanel.xhtml

I think the configuration is correct.

Finally the last step was to create the login form in the index.xhtml page:

<form method="post" action="j_security_check" name="loginForm">

        <h:outputLabel id="userNameLabel" for="j_username" value="Enter your em@il:"/>
        <h:inputText id="j_username" autocomplete="off" />
        <br/>
        <h:outputLabel id="passwordLabel" for="j_password" value="Enter your em@il password:"/>
        <h:inputSecret id="j_password" autocomplete="off"/>
        <br/>
        <h:commandButton type="submit" value="Login"/>
        <h:commandButton type="reset" value="Clear"/>
  </form>

The program builds fine, but i have the following problems:

1- When i try to login as user or as administrator(visitors don't need to login), i get redirected to the error.xhtml page and in the console i see an exception:

SEVERE: SEC1112: Cannot validate user [admin@gmail.com] for JDBC realm. WARNING: Web login failed: Login failed: javax.security.auth.login.LoginException: Security Exception WARNING: PWC4011: Unable to set request character encoding to UTF-8 from context /CHAPTER_12_x_Container_Managed_Authentication_and_Authorization, because request parameters have already been read, or ServletRequest.getReader() has already been called

2- When i try to navigate to some of the pages via the URL, nothing happens. I think that is ok, but when i try to visit visitorpanel.xhtml, it should let me, because there is no need to be logged in to see it. Do i need to remove that page from the security configuration if a want every body to see it?

3- Also i am curious why i cant use the tag h:form instead of just form, when i implement the login?

Ill really appreciate some help, ive been a few hours reading the first chapters of the book and trying to implement, my own example but i got stuck. I think i am close to the solution.

Update

I changed the Default principal to be the visitors user name. But it still don't work

enter image description here

And i also added some more options to my Realm configuration

enter image description here

But when i try to login i still see an exception that says:

SEVERE: SEC1112: Cannot validate user [admin@gmail.com] for JDBC realm. WARNING: Web login failed: Login failed: javax.security.auth.login.LoginException: Security Exception WARNING: PWC4011: Unable to set request character encoding to UTF-8 from context /CHAPTER_12_x_Container_Managed_Authentication_and_Authorization, because request parameters have already been read, or ServletRequest.getReader() has already been called

I still don't know what is missing.

-Could it be that the table name should not be upper case?

-Could it be that the columns names should not be upper case?

-Could it be that the tables are created wrong?

-Could it be that i cant use PASSWORD as a column name, because it makes some kind of conflict?

I really don't understand why that exception. I pinged the database from the admin panel, and all seem to be correct.

Can someone help me figuring this one out?

Update 2

I changed the 'javax.enterprise.system.core.security' logging option to level FINE, to have more information when exceptions occur, this was the result when i tried to login:

FINE: Intercept Entry: intercept: SOAP defaultServerID: null defaultClientID: null FINE: ID Entry: module class: com.sun.xml.wss.provider.ClientSecurityAuthModule id: XWS_ClientProvider type: client request policy: javax.security.auth.message.MessagePolicy@e95a72 response policy: javax.security.auth.message.MessagePolicy@310a6d options: {signature.key.alias=s1as, debug=false, dynamic.username.password=false, encryption.key.alias=s1as} FINE: ID Entry: module class: com.sun.xml.wss.provider.ClientSecurityAuthModule id: ClientProvider type: client request policy: javax.security.auth.message.MessagePolicy@1829770 response policy: javax.security.auth.message.MessagePolicy@a4461e options: {signature.key.alias=s1as, debug=false, dynamic.username.password=false, encryption.key.alias=s1as, security.config=C:\jeeAplicationServer\glassfishv3\glassfish\domains\domain1/config/wss-server-config-1.0.xml} FINE: ID Entry: module class: com.sun.xml.wss.provider.ServerSecurityAuthModule id: XWS_ServerProvider type: server request policy: javax.security.auth.message.MessagePolicy@f79c86 response policy: javax.security.auth.message.MessagePolicy@454bf7 options: {signature.key.alias=s1as, debug=false, encryption.key.alias=s1as} FINE: ID Entry: module class: com.sun.xml.wss.provider.ServerSecurityAuthModule id: ServerProvider type: server request policy: javax.security.auth.message.MessagePolicy@17e85e4 response policy: javax.security.auth.message.MessagePolicy@1887906 options: {signature.key.alias=s1as, debug=false, encryption.key.alias=s1as, security.config=C:\jeeAplicationServer\glassfishv3\glassfish\domains\domain1/config/wss-server-config-1.0.xml} FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization FINE: [Web-Security] hasUserDataPermission perm: (javax.security.jacc.WebUserDataPermission /j_security_check POST) FINE: [Web-Security] hasUserDataPermission isGranted: true FINE: Logging in user [admin@gmail.com] into realm: DBRealm using JAAS module: jdbcRealm FINE: Login module initialized: class com.sun.enterprise.security.auth.login.JDBCLoginModule SEVERE: SEC1112: Cannot validate user [admin@gmail.com] for JDBC realm. FINE: Cannot validate user javax.security.auth.login.LoginException: Unable to connect to datasource jdbc/security for database user user. at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.getConnection(JDBCRealm.java:550) at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.isUserValid(JDBCRealm.java:393) at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.authenticate(JDBCRealm.java:311) at com.sun.enterprise.security.auth.login.JDBCLoginModule.authenticate(JDBCLoginModule.java:72) at com.sun.enterprise.security.auth.login.PasswordLoginModule.authenticateUser(PasswordLoginModule.java:90) at com.sun.appserv.security.AppservPasswordLoginModule.login(AppservPasswordLoginModule.java:141) 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:341) at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:199) at com.sun.enterprise.security.auth.login.LoginContextDriver.login(LoginContextDriver.java:152) at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:479) at com.sun.web.security.RealmAdapter.authenticate(RealmAdapter.java:418) at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:264) at org.apache.catalina.authenticator.AuthenticatorBase.processSecurityCheck(AuthenticatorBase.java:1015) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:614) at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:615) at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97) at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185) at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:325) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226) at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165) at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791) at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693) at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954) at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170) at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76) at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) at com.sun.grizzly.ContextTask.run(ContextTask.java:69) at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330) at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309) at java.lang.Thread.run(Thread.java:662) Caused by: javax.naming.NamingException: Lookup failed for 'jdbc/security' in SerialContext [Root exception is javax.naming.NameNotFoundException: security not found] at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:442) at javax.naming.InitialContext.lookup(InitialContext.java:392) at javax.naming.InitialContext.lookup(InitialContext.java:392) at com.sun.enterprise.connectors.service.ConnectorResourceAdminServiceImpl.lookup(ConnectorResourceAdminServiceImpl.java:203) at com.sun.enterprise.connectors.ConnectorRuntime.lookupNonTxResource(ConnectorRuntime.java:440) at com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm.getConnection(JDBCRealm.java:538) ... 44 more Caused by: javax.naming.NameNotFoundException: security not found at com.sun.enterprise.naming.impl.TransientContext.doLookup(TransientContext.java:197) at com.sun.enterprise.naming.impl.TransientContext.lookup(TransientContext.java:168) at com.sun.enterprise.naming.impl.TransientContext.lookup(TransientContext.java:172) at com.sun.enterprise.naming.impl.SerialContextProviderImpl.lookup(SerialContextProviderImpl.java:58) at com.sun.enterprise.naming.impl.LocalSerialContextProviderImpl.lookup(LocalSerialContextProviderImpl.java:101) at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:430) ... 49 more

FINE: JAAS authentication aborted. WARNING: Web login failed: Login failed: javax.security.auth.login.LoginException: Security Exception FINE: [Web-Security] Policy Context ID was: CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization FINE: [Web-Security] hasUserDataPermission perm: (javax.security.jacc.WebUserDataPermission /error.xhtml GET) FINE: [Web-Security] hasUserDataPermission isGranted: true

Update 3

Maybe there is something wron with the connection pool. This is how my connection pool looks like:

enter image description here

enter image description here

enter image description here

I don't have much properties, maybe something is missing?

Also now i created a JDBC resource, that looks like this:

enter image description here

(The JNDI name in the Realm, was changed to jdbc/studydb)

My persistence.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="CHAPTER x 12 Container Managed Authentication and Authorization">
        <jta-data-source>jdbc/studydb</jta-data-source>     
        <class>entities.User</class>
        <class>entities.Group</class>
    </persistence-unit>
</persistence>

I think i made some progress, now the exception i see is:

> SEVERE: jdbcrealm.invaliduserreason
>      FINE: Cannot validate user
>      java.sql.SQLSyntaxErrorException: Schema 'ADMIN' does not exist
>       at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown
> Source)
>      ....
>      
>      Caused by: org.apache.derby.client.am.SqlException: Schema 'ADMIN' does not exist
>       at org.apache.derby.client.am.Statement.completeSqlca(Unknown Source)
>      ...
>      FINE: JAAS authentication aborted.
>      WARNING: Web login failed: Login failed: javax.security.auth.login.LoginException: Security Exception
>      FINE: [Web-Security] Policy Context ID was: CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization
>      FINE: [Web-Security] hasUserDataPermission perm: (javax.security.jacc.WebUserDataPermission /error.xhtml GET)
>      FINE: [Web-Security] hasUserDataPermission isGranted: true
>      WARNING: PWC4011: Unable to set request character encoding to UTF-8 from context 
> /CHAPTER_12_x_Container_Managed_Authentication_and_Authorization, 
> because request parameters have already been read, or 
> ServletRequest.getReader() has already been called

Update 4

I changed the database, it was wrongly organized so i made some changes in my entities:

@Entity
    @Table(name="USERS", schema="ADMIN")
    public class User implements Serializable {

        private static final long serialVersionUID = -1244856316278032177L;
        @Id 
        @Column(nullable = false)
        private String userid;  

        @Column(nullable = false)
        private String password;

        @ManyToOne
        @JoinTable(name="USER_GROUP",schema="ADMIN", joinColumns = @JoinColumn(name="userid", referencedColumnName="userid"), inverseJoinColumns=@JoinColumn(name="groupid", referencedColumnName= "groupid") )
        private Group group;
        //GET & SET METHODS

@Entity @Table(name="GROUPS", schema="ADMIN") public class Group implements Serializable {

private static final long serialVersionUID = -7274308564659753174L;
@Id
@Column(nullable = false)
private String groupid;

@OneToMany(mappedBy="group")
private Set<User> users;

//GET & SET METHODS

So i had also to edit the DBRealm, now it looks like this:

enter image description here

But when i login i get again an exception:

FINE: [Web-Security] Policy Context ID was: CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization
FINE: [Web-Security] hasUserDataPermission perm: (javax.security.jacc.WebUserDataPermission /j_security_check POST)
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: Logging in user [user@gmail.com] into realm: DBRealm using JAAS module: jdbcRealm
FINE: Login module initialized: class com.sun.enterprise.security.auth.login.JDBCLoginModule
SEVERE: SEC1111: Cannot load group for JDBC realm user [user@gmail.com].
FINE: Cannot load group
java.sql.SQLSyntaxErrorException: Column 'USERID' is either not in any table in the FROM list or appears within a join specification and is outside the scope of the join specification or appears in a HAVING clause and is not in the GROUP BY list. If this is a CREATE or ALTER TABLE  statement then 'USERID' is not a column in the target table.
....
....
Caused by: org.apache.derby.client.am.SqlException: Column 'USERID' is either not in any table in the FROM list or appears within a join specification and is outside the scope of the join specification or appears in a HAVING clause and is not in the GROUP BY list. If this is a CREATE or ALTER TABLE  statement then 'USERID' is not a column in the target table.
    at org.apache.derby.client.am.Statement.completeSqlca(Unknown Source)
....
....
FINE: JAAS login complete.
FINE: JAAS authentication committed.
FINE: Password login succeeded for : user@gmail.com
FINE: permission check done to set SecurityContext
FINE: Set security context as user: user@gmail.com
FINE: [Web-Security] Policy Context ID was: CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization
FINE: [Web-Security] hasUserDataPermission perm: (javax.security.jacc.WebUserDataPermission  GET)
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: permission check done to set SecurityContext
FINE: SecurityContext: setCurrentSecurityContext method called

FINE: [Web-Security] Policy Context ID was: CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization
FINE: [Web-Security] hasUserDataPermission perm: (javax.security.jacc.WebUserDataPermission /adminpanel.xhtml GET)
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: [Web-Security] Policy Context ID was: CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization
FINE: [Web-Security] Generating a protection domain for Permission check.
FINE: [Web-Security] Checking with Principal : user@gmail.com
FINE: [Web-Security] Checking with Principal : visitors
FINE: [Web-Security] Checking with Principal : users
FINE: [Web-Security] Checking with Principal : administrators
FINE: [Web-Security] Codesource with Web URL: file:/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization/CHAPTER_x_12_Container_Managed_Authentication_and_Authorization
FINE: [Web-Security] Checking Web Permission with Principals : user@gmail.com, visitors, users, administrators
FINE: [Web-Security] Web Permission = (javax.security.jacc.WebResourcePermission /adminpanel.xhtml GET)
FINE: [Web-Security] hasResource isGranted: true
FINE: [Web-Security] hasResource perm: (javax.security.jacc.WebResourcePermission /adminpanel.xhtml GET)
FINE: SecurityContext: setCurrentSecurityContext method called

WARNING: Resource not found: com/sun/enterprise/v3/admin/adapter/theme/com/sun/webui/jsf/suntheme/images/masthead/masthead_button_over.gif
javing
  • 12,307
  • 35
  • 138
  • 211
  • 1
    Thanks sfrj for sticking with this and providing so much detail of your experience! I'm having troubles with Glassfish and JDBC realm security, too. The details in your question are of great use to me! I do see some things that look suspicious or missing, but I don't yet have my JDBC authentication working, so I'll wait to provide details from my experience. – Jason Jun 23 '13 at 18:02
  • @Jason Hi Jason, Glad you found this interesting. I asked this back in 2011. If you have a look at the answers maybe you are able to put the missing pieces together(There are also good answers). In the case you may be interested to use a file realm instead of a jdbc one, have a look at this video tutorial I made time ago in my blog: http://javing.blogspot.ie/2012/05/here-in-this-video-you-can-see-how-i.html – javing Jun 24 '13 at 17:25
  • thanks for being so clear and detailed. This is better than most tutorials on Glassfish authentication out there.. – Oliver Watkins Nov 28 '13 at 09:17
  • I am having issue with migration from 4.0 to 4.1.1 http://stackoverflow.com/questions/40686737/migration-from-glassfish-4-0-to-glassfish-4-1-1-jdbc-realm-issue – Bikram Nov 27 '16 at 01:37

4 Answers4

18

There are a few missing bits in your configuration:

  • The password is stored in cleartext in the database. This is most likely incorrect. Glassfish 3.1 uses the SHA-256 algorithm by default, so the JDBC Realm would fail to authenticate users since the stored value in the database would not match the digest created by the Realm. You'll need to specify an explicit digest algorithm in the realm configuration, or rely on the default. Also, you'll need to ensure that your application creates the digests when a new user is created or when his/her password is modified. If you want to store the password in plaintext, you must specify the value of "none" for the digest algorithm.
  • Specifying the digest algorithm alone is insufficient. You'll need to specify the encoding in which the digest is stored (since, a digest is simply a sequence of bytes, and may not be stored as a plain ASCII sequence). Glassfish supports Hex and Base64 encoding, and uses Hex encoding by default. Your app should therefore apply the same encoding as configured in the realm, before storing the password digest. Note that, when you specify a digest algorithm of "none" to store the passwords in plain text, you need not encode the stored password (and likewise, you need not specify an encoding either); atleast this has been my observation from reading the Glassfish sources.
  • Additionally, the user-group mapping appears to be 1:1 at the moment. You might want to use a separate join table to allow for 1:N mappings between groups and users.
  • You'll also need to ensure that the option of "default principal to role mapping" is enabled. Without this option, you'll need to manually map the roles in web.xml to users and groups in your realm.

On the topic of using form instead of h:form, the underlying reason is that the JSF runtime would not allow you to specify the action attribute of the h:form tag. This value is set by the JSF runtime, when encoding the response, and therefore, you'll be unable to specify the value of j_security_check when you use the h:form tag. The documentation states this quite explicitly:

The value of the "action" attribute must be the result of passing the view identifier of the current view to the getActionURL() method of the ViewHandler for this application, then passing that String to the encodeActionURL() method on the ExternalContext.

Update

Based on the posted stack trace, I could infer that the JNDI datasource (specified in the JNDI field) to be used by the Realm, is not available in the Glassfish domain. One of the prerequisites for the JDBC Realm is to have a JNDI datasource registered in the Glassfish domain configuration, whose connection pool is used to connect to the underlying database.

Following is an snippet of my Glassfish domain configuration file (domain.xml) where a connection pool (GalleriaPool) is used by a JNDI DataSource (jdbc/galleriaDS), that is eventually used by the JDBC Realm (GalleriaRealm):

<domain log-root="${com.sun.aas.instanceRoot}/logs" application-root="${com.sun.aas.instanceRoot}/applications" version="12">
  <resources>
    ...
    <jdbc-connection-pool validation-table-name="SYSIBM.SYSDUMMY1" driver-classname="" datasource-classname="org.apache.derby.jdbc.ClientDataSource40" res-type="javax.sql.DataSource" description="" name="GalleriaPool" is-connection-validation-required="true" fail-all-connections="true" ping="true">
      <property name="User" value="APP"></property>
      <property name="DatabaseName" value="GALLERIA"></property>
      <property name="RetrieveMessageText" value="true"></property>
      <property name="CreateDatabase" value="true"></property>
      <property name="Password" value="APP"></property>
      <property name="ServerName" value="localhost"></property>
      <property name="Ssl" value="off"></property>
      <property name="SecurityMechanism" value="4"></property>
      <property name="TraceFileAppend" value="false"></property>
      <property name="TraceLevel" value="-1"></property>
      <property name="PortNumber" value="1527"></property>
      <property name="LoginTimeout" value="0"></property>
    </jdbc-connection-pool>
    <jdbc-resource pool-name="GalleriaPool" description="" jndi-name="jdbc/galleriaDS"></jdbc-resource>
  </resources>
  <servers>
    <server name="server" config-ref="server-config">
    ...
      <resource-ref ref="jdbc/galleriaDS"></resource-ref>
    </server>
  </servers>
  ...
  <configs>
    <config name="server-config">
    ...
      <security-service>
        <auth-realm name="GalleriaRealm" classname="com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm">
          <property name="jaas-context" value="jdbcRealm"></property>
          <property name="encoding" value="Hex"></property>
          <property name="password-column" value="PASSWORD"></property>
          <property name="datasource-jndi" value="jdbc/galleriaDS"></property>
          <property name="group-table" value="USERS_GROUPS"></property>
          <property name="charset" value="UTF-8"></property>
          <property name="user-table" value="USERS"></property>
          <property name="group-name-column" value="GROUPID"></property>
          <property name="digest-algorithm" value="SHA-512"></property>
          <property name="user-name-column" value="USERID"></property>
        </auth-realm>
        ...
      </security-service>
    </config>
    ...
  </configs>
  ...
</domain>

Update #2 - Getting the SQL statements executed by the JDBC Realm against Derby

It looks like the structure of the SQL query does not match the database model that you've prepared. You could take a look at the SQL statements executed by the JDBC realm against the Derby instance via the derby.language.logStatementText system property. This property can be set to true as a static value in the derby.properties file, and it will take effect when you restart the Derby instance. The derby.properties file needs to have the following entry:

derby.language.logStatementText=true

and this file must be placed in the current working directory of the Derby instance. The current working directory is often the directory that contains the Derby databases, and can be explicitly specified using the derby.system.home JVM argument, during Derby startup:

-Dderby.system.home=C:\derby

All SQL statements executed by Derby, will now be logged in the derby.log file.

Based on the provided information, I'm under the impression that there is a separate table called GROUPS which is used to store the Group info, and this is different from the join table - USER_GROUP. In this scenario, your realm must be configured to have the Group table as USER_GROUP and not GROUP; you can confirm this by looking at the SQL queries issued by the JDBC realm.

To clarify the above point, the Group field in the JDBC realm configuration is not used to specify the table that stores the group information. Instead, it is used to specify the table that stores the Group-User mappings. In a 1:1 mapping, the Group table can store this information, but in a 1:M or typically in a M:M scenario, you would have a separate table that contains the mapping. The SQL query issued by the JDBC realm, uses the mapping table and not the actual group table (if they're different) to determine the groups that a user belongs to.

Community
  • 1
  • 1
Vineet Reynolds
  • 76,006
  • 17
  • 150
  • 174
  • So i should implement some registration that uses the SHA-256 algorithm by using the digest word that i give in the realm configuration interface. Right? I better create an EJB to do that before persisting the new registered users. Also i have a doubt about the default Principal. What value should i put in the field 'Default Principal'? Should i use the value of a the role, or the user name of some user registered with the role 'visitors'? – javing Oct 30 '11 at 13:58
  • @sfrj, you can use plain text - see the notes in my answer. And yes, if you choose to use SHA-256, your registration page must delegate the generation of the SHA-256 digest to a bean or an EJB. The default principal to role mapping is a feature in glassfish. It is useful in a scenario like yours where the names of the roles in `web.xml` are the same as the groups and users in the actual realm. Without that option, you'll need to add role to user/group mappings in `glassfish-web.xml` and other vendor deployment descriptors. – Vineet Reynolds Oct 30 '11 at 14:05
  • @sfrj, if you want to take a look at a complete example, involving registration, change password etc. that relies on a GF JDBC realm, and JPA, you can find one [here](https://bitbucket.org/VineetReynolds/java-ee-6-galleria). Take a look the `User` and `Group` classes in the `galleria-ejb` module, and also the `Login.xhtml` and `Signup.xhtml` pages in the `galleria-jsf` module. – Vineet Reynolds Oct 30 '11 at 14:08
  • Thanks for the example, i cloned it with mercurial to my hard disk, i will have a look at it, but i think that is a bit advanced for me. I said "none"(with no quotes) to the digest algorithm i want to keep this first example as simple as possible. Also at the glassfish admin console i set default principal to visitor@gmail.com and the password 12345. I restarted the server, but the login mechanism still don't work as spected. What is missing? Is there somthing wrong with my tables? I don't understand – javing Oct 30 '11 at 14:28
  • @sfrj, I think I misunderstood your question about the default principal. Which Glassfish configuration are you referring to? I don't remember needing one with an explicit user Id and password. – Vineet Reynolds Oct 30 '11 at 14:35
  • I reffer to the Default Principal option, found when you click on Security in the Glassfish server admin panel(See the image i just updated above) – javing Oct 30 '11 at 14:50
  • I rode somewhere that if the column for the password is called password, it sometimes creates trouble. Should i change my entities and change the variable name to psw, or similar? You think that could be a reason? – javing Oct 30 '11 at 14:56
  • @sfrj, it could, if columns are named as `USER`, `PASSWORD` etc and if these columns are reserved or special words in the database. If the SQL statement can be executed, then the realm will always authenticate the user. For enabling default role-principal mapping: `To turn on the default mapping, choose Configuration -> Security in the admin console. Click Enabled next to Default Principal to Role Mapping and Save.` That's all there is to it. – Vineet Reynolds Oct 30 '11 at 16:07
  • I enabled Default Principal to Role Mapping, and i also made some changes to the realm configuration(See my update above). I still see the exception in the console. Don't know what should i do? Does this have to do something with the encoding? – javing Oct 31 '11 at 10:49
  • @sfrj, UTF-8 is an invalid encoding; however, it would be a valid charset value. Hex and Base64 are valid encodings, if you supply them. You can leave the field blank when digest algorithm is "none". The failure appears to be in the `isUserValid` method of the [JDBCRealm](http://java.net/projects/glassfish/sources/svn/content/tags/3.1.1-b12/security/core/src/main/java/com/sun/enterprise/security/auth/realm/jdbc/JDBCRealm.java?rev=50587) class. Setting the level of the `javax.enterprise.system.core.security` logger to `FINE` in the GF logging.properties should give you the stack trace. – Vineet Reynolds Oct 31 '11 at 12:29
  • Ok i tried again, this time i changed the charset to UTF-8 and i left the encoding field blank. I also set the 'javax.enterprise.system.core.security' option of the log to level 'FINE' i restarted then the server and run again. The result when i loged in was a more detailed info at the console that says more information(See the update above) – javing Oct 31 '11 at 13:26
  • @sfrj, ok, see my updated answer. The `domain.xml` snippet is from my GF domain that I use for my project posted above. Also, omit the Database User and Password in your Realm configuration; specify those in the connection pool configuration. – Vineet Reynolds Oct 31 '11 at 13:44
  • I am a bit confused, i think my connection pool should be ok,because i was able to use JPA to generate the Tables from my entities,but i would like to check again. What is the name of the file i should edit? I cant see that in my project explorer. But from the glassfish admin panel i can see it at 'JDBC>connection pools' – javing Oct 31 '11 at 13:57
  • @sfrj, `domain.xml` is present in the _domainHome/config_ directory. Any changes made in the Admin Console will be reflected in this file. I don't think the problem is with the connection pool, but with the JNDI name that you've specified. In your case, the JNDI name of the datasource that you've created, should be `jdbc/security` (in domain.xml, it should be specified in the `jdbc-resource` and `resource-ref` tags for the `jndi-name` and `ref` attributes). – Vineet Reynolds Oct 31 '11 at 14:08
  • I made a 3rd update to my question(See above) with the information about my connection pool(I created it using the admin panel, not an .xml file) I dont understand what is wrong? Maybe there is some property missing? I dont have a file called domain.xml in my application, where is it? – javing Oct 31 '11 at 14:13
  • @sfrj, you've created a JDBC connection pool, but I'm not sure if you've created a JDBC Resource (or a datasource) that uses this pool. JDBC resources can be bound to the JNDI tree, but not the connection pool. If you're comfortable using `asadmin`, refer to the [GF admin guide on how to create a JDBC resource](http://download.oracle.com/docs/cd/E18930_01/html/821-2416/ggndx.html#ggnda). Also, the domain.xml file is not part of an application, but a domain. You'll find one in `C:\glassfish3\glassfish\domains\domain1\config` if `C:\glassfish3\glassfish\domains\domain1` is your GF domain. – Vineet Reynolds Oct 31 '11 at 14:25
  • I noticed i dont have a JDBC resource(Im confused now i dont understand how i was able to create tables?), so i am creating one. Should i use 'jdbc/studydb'? (It is the name of my database), also i think i should change the JDNI in my realm from 'jdbc/security' to 'jdbc/studydb' right? Should i add other properties to the JDBC resource? – javing Oct 31 '11 at 14:37
  • @sfrj, well, only the JNDI name and connection pool name is sufficient to create a JDBC resource. The JNDI name chosen will be bound to the global JNDI tree, and yes, like you've inferred, the same named must be used in the realm configuration. The name can be pretty much anything, preferably it should start with `jdbc` (for identifying it as a JDBC resource). You might have a connection pool configured in your `persistence.xml` file, and you might be using RESOURCE_LOCAL persistence unit, allowing for the tables to be created. – Vineet Reynolds Oct 31 '11 at 14:44
  • I made some changes in my realm. The JDNI name i provided is jdbc/studydb, then i created the JDBC resource with that same JDNI name. I restarted and run the application and now i see a different exception. It is an SQLSyntaxException that says: SEVERE: jdbcrealm.invaliduserreason FINE: Cannot validate user java.sql.SQLSyntaxErrorException: Schema 'ADMIN' does not exist ... Caused by: org.apache.derby.client.am.SqlException: Schema 'ADMIN' does not exist – javing Oct 31 '11 at 14:46
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/4616/discussion-between-vineet-reynolds-and-sfrj) – Vineet Reynolds Oct 31 '11 at 14:49
  • Sorry i didn't see your last comment. I am in the chat now. – javing Oct 31 '11 at 15:11
  • As we disscussed in the chat, i managed to create the ADMIN scheme and now my program uses it instead of USER. I think i am really close to make it work, but it looks like something is wrong with the tables now, when i try to login. I dont know what should be the best practice to create them. – javing Nov 01 '11 at 14:28
  • This is what the exception sais when i try to login: **SEVERE: SEC1111: Cannot load group for JDBC realm user [admin@gmail.com]. FINE: Cannot load group java.sql.SQLSyntaxErrorException: Column 'EMAIL' is either not in any table in the FROM list or appears within a join specification and is outside the scope of the join specification or appears in a HAVING clause and is not in the GROUP BY list. If this is a CREATE or ALTER TABLE statement then 'EMAIL' is not a column in the target table.** – javing Nov 01 '11 at 14:28
  • @sfrj, can you verify whether [the queries listed in this answer](http://stackoverflow.com/questions/6809081/glassfish-jdbc-realm-group-membership/6809340#6809340) would be valid in your schema? The JDBC realm issues two queries at most to authenticate the user. – Vineet Reynolds Nov 01 '11 at 15:51
  • Yes i verify. My database schema ADMIN currently uses exactly those queries to be created. Now that i managed to create the DB exactly as disscussed in the chat (i did it using JPA annotations), i tried to login again but i get a new exception(see "update 4"). I think im very near the solution, now everything is configured ok. What could be the reason? – Also i would like to mention that the squema ADMIN does not have a table called SEQUENCE that was by default in the USER schema. Could that have something to do with it? – javing Nov 02 '11 at 15:05
  • @sfrj Without going through the whole QuestionCommentTrace: Don't you need to set the name of the JoinColumn into the group table field of the JDBC realm configuration (this is what I do in my projects)? I think the exception is thrown because there is no column UserId in your group table (but it is in the join table!). – Matt Handy Nov 02 '11 at 16:34
  • @Matt Handy I am a bit confused, so you think i should leave that blank? Or maybe i should set the GROUP table to in the realm to be GROUP_USER(The join column), so it will for sure find the column USERID.Should i do that? Do you think is that a good idea? – javing Nov 02 '11 at 16:50
  • @sfrj The second option. Set GROUP_USER as group tabe in your realm configuration. Otherwise Glassfish doesn't know anything about user/group relations. – Matt Handy Nov 02 '11 at 18:54
  • @Vinnet Reynolds Its being already 3 days struggling with it but i think i am close. The question got a bit too long and maybe confusing, so i decided to accept the question(almost all the problems are solved) and open a new question with the current issue at: http://stackoverflow.com/questions/7994068/cannot-load-group-for-jdbc-realm – javing Nov 03 '11 at 11:30
  • @Matt Handy Thank you Matt too your comment was useful too, i did as you said and changed the configuration in the realm to use the Join table, but remain with the same problem. See this link: http://stackoverflow.com/questions/7994068/cannot-load-group-for-jdbc-realm – javing Nov 03 '11 at 11:31
  • I am having issue with migration from 4.0 to 4.1.1 http://stackoverflow.com/questions/40686737/migration-from-glassfish-4-0-to-glassfish-4-1-1-jdbc-realm-issue – Bikram Nov 27 '16 at 01:35
3

On top of the answer from Vineet , i also noticed that the option security Manager is not checked in the Security tab of GlassFish, that option should be enabled in order to use Security in your Realm.

Another thing if you are using a JDBC JNDI name you don't need to supply a username/password to the JDBC realm

Cristiano Fontes
  • 4,920
  • 6
  • 43
  • 76
  • i checked the box for 'security Manager', nothing nee happens, still have the same exception in the console. Regarding to the database user and password, i don't think it will disturb to fill those fields right? – javing Oct 31 '11 at 13:41
  • Another thing this error message is clear: java.sql.SQLSyntaxErrorException: Schema 'ADMIN' does not exist, does it exist ? the Schema ? – Cristiano Fontes Oct 31 '11 at 16:21
  • Also another thing, your Tables are not following the Standard JDBC realm ones, they should not have maps like that and the key to the Groups table should be the exact same ID that you use to Login... you are using email as login but the Primary key to the groups DB is not email is ID. you could try to change that to be email. and see if it fixes it. – Cristiano Fontes Oct 31 '11 at 16:26
  • I did restart the server, currently i am fixing the schema issue, i noticed that too. Regarding to the tables, i see no reason why shouldnt work? Do you know some link where i could see what is the best practice to make those tables? – javing Nov 01 '11 at 13:01
  • The JDBCrealm is just an example of how to create a JDBC realm so it's not 100% customizable, I had the same issues you did a month ago... I think it was made with a join in the select using the login of the user to get the groups so other table setups will not work... if you need another setup you got to create your own realm, that is what I did. Here is a link with the "Normal" table. http://blogs.oracle.com/swchan/entry/jdbcrealm_in_glassfish_with_mysql – Cristiano Fontes Nov 01 '11 at 13:09
2

I already did this on Sailfin (based on Glassfish 2).

First, I'm not seeing any sun-web.xml file that maps the roles defined in your web.xml file to the groups defined the database.

Second, according to this tutorial: http://codepimpsdotorg.blogspot.com/2007/12/glassfish-jdbc-realm-authentication.html you must specify a digest algorithm ("none" is not an option, not sure if it remains the same in Glassfish 3).

Third, the tables and columns need to be in a particular order, we are using the following structure:

@Entity
@Table(name = "AuthenticationUser")
public class UserEntity implements Serializable, Identifiable
{   
    private static final long serialVersionUID = -1213555368237839900L;

    @Id
    @Column(name = "name", length = 20)
    private String itsName;

    @Column(name = "password", length = 1024)
    private String itsPassword;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "groupName", referencedColumnName = "name")
    private GroupEntity itsGroupEntity;
...

And

@Entity
@Table(name = "AuthenticationGroup")
public class GroupEntity implements Serializable, Identifiable
{
    private static final long serialVersionUID = -1213554368237839900L;

    private static final String USER_GROUP_COLUMN = "itsGroupEntity";

    @Id
    @Column(name = "name", length = 20)
    private String itsName;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
            mappedBy=USER_GROUP_COLUMN)
    private List<UserEntity> itsUsers;
...

And to define the realm:

user-table=AuthenticationUser
user-name-column=name
password-column=password
group-table=AuthenticationUser
group-name-column=groupName

IMPORTANT: The user name and group name are belonging to the same table! so the table AuthenticationGroup is only for our internal use, but is not used by Glassfish.

German
  • 3,560
  • 7
  • 32
  • 34
2

I ran into the same issue.

I solved this problem by renaming the password to(User_password) and the userName (User_name) fields in the table (anything other than username and password will do), somehow using "userName" and "password" causes some conflict while performing authentication using Realms.

Also put Digest Algorithm: = none in case you are storing the password as plain text.

In the Security menu, Enable the Default Principal to Role Mapping.

Hope this helps,

raman
  • 21
  • 1
  • Yeah sometimes the name of the column might cause conflict. Depends on the database, good we solved this one, it was not easy :) – javing Nov 15 '11 at 16:07