The JDBCRealm
In the Glassfish Administration Console, go to Configurations -> server-config -> Security -> Realms -> and select "New".We're going to choose com.sun.enterprise.security.ee.auth.realm.jdbc.JDBCRealm.
The jaas.context name of the new realm is "jdbcRealm".
A jdbcRealm needs both a user table and a group table. The group table should store both userids as well as groupids. In other words,
- the group table doesn't only contain groups, and
- has a composite primary key, consisting of userid and groupid.
Database Schema
The user table is called "mm_admin" in my case, and the groups table is called "mm_admin_mm_groups".I have created the "mm_groups" table to store extra information for groups, but that table is ignored by the jdbcRealm.
Some serious disadvantages of the jdbcRealm are:
- groups are cached, which means if a user changes groups, the Glassfish Server needs to be rebooted.
- flexibility is about zero, for instance if I have an expiration date attached to my usertable, I'm out of luck.
The flexibleJDBCRealm
The great advantage of the flexibleJDBCRealm is that you can add SQL queries that are run to retrieve passwords and groups.Instructions
Well, according to instructions found here6, you have to:- download the flexiblejdbcrealm-deploy-1.2-all.jar
- put it into glassfish/domains/domain1/lib directory
- change glassfish/domains/domain1/config/login.conf to add flexibleJdbcRealm {
org.wamblee.glassfish.auth.FlexibleJdbcLoginModule required;
}; - reboot the glassfish
- In the Glassfish Administration Console, go to Configurations -> server-config -> Security -> Realms -> and select "New"
- enter the class name org.wamblee.glassfish.auth.FlexibleJdbcRealm
- set the properties, for example like in the image above.
Properties
The properties that can be entered could use a little more explaining compared to what is available at [6].- password.digest
- uses MessageDigest(String algorithm). Possible values are "PLAIN", "MD2", "MD5", "SHA-1", "SHA-256", "SHA-384" and SHA-512.
- password.encoding
- the encoding, I find HEX to be the most useful. Possibile values are "TEXT", "HEX" and "BASE64". A variation of "HEX" is "HEX:<digits>", for example "HEX:40". This padds the beginning of the encoding with zeros to reach the length required. For example MySQL itself always creates passwords using the SHA() function to 40 hex digits also padding the beginning with zeros.
- sql.groups
- select groupid from mm_admin_mm_groups where name in (?)
- sql.password
- select passwd from mm_admin where name in (?) and validuntil > now()
- jaas.context
- flexibleJdbcRealm
- datasource.jndi
- of course, the connection string to the database
Logging
In glassfish/domains/domain1/config/logging.properties, add: org.wamblee.level=FINEST.Application Configuration
My web.xml would look like thusly:<?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>Administration</display-name>
<web-resource-collection>
<web-resource-name>Administration REST Resources</web-resource-name>
<description/>
<url-pattern>/resources/administration/*</url-pattern>
<url-pattern>/administration/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>deputy</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>deputyRealm</realm-name>
</login-config>
<security-role>
<description/>
<role-name>deputy</role-name>
</security-role>
</web-app>
My glassfish-web.xml looks like:<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>Administration</display-name>
<web-resource-collection>
<web-resource-name>Administration REST Resources</web-resource-name>
<description/>
<url-pattern>/resources/administration/*</url-pattern>
<url-pattern>/administration/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>deputy</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>deputyRealm</realm-name>
</login-config>
<security-role>
<description/>
<role-name>deputy</role-name>
</security-role>
</web-app>
<?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="">
<security-role-mapping>
<role-name>deputy</role-name>
<group-name>deputy</group-name>
</security-role-mapping>
<class-loader delegate="true"/>
<jsp-config>
<property name="keepgenerated" value="true">
<description>Keep a copy of the generated servlet class' java code.</description>
</property>
</jsp-config>
</glassfish-web-app>
In this file you can create a mapping between security roles and groups.<!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="">
<security-role-mapping>
<role-name>deputy</role-name>
<group-name>deputy</group-name>
</security-role-mapping>
<class-loader delegate="true"/>
<jsp-config>
<property name="keepgenerated" value="true">
<description>Keep a copy of the generated servlet class' java code.</description>
</property>
</jsp-config>
</glassfish-web-app>
Declarative Security
An example of declarative security using Annotations:@DeclareRoles("deputy")
@RolesAllowed("deputy")
@Stateless
@Path("/administration/worldattributes")
public class WorldattributesBean extends AbstractFacade<Worldattribute>
{
This means the role definition in the web.xml can be removed.@RolesAllowed("deputy")
@Stateless
@Path("/administration/worldattributes")
public class WorldattributesBean extends AbstractFacade<Worldattribute>
{
In the example above, the security role is applied to the entire Bean, so for each method in the bean, instead of on each method that requires the role.
Programmatic Security
Unfortunately, some fine-grained security can only be done with programmatic means, for example determining if a person who has authenticated themselves owns a certain object that needs mutated upon. In my example, this info in a Rest service can be obtained through a SecurityContext:import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.Context;
...
@POST
@Override
@Consumes(
{
"application/xml", "application/json"
})
public void create(Worldattribute entity, @Context SecurityContext sc)
{
itsLog.info("create");
final String name = sc.getUserPrincipal().getName();
import javax.ws.rs.core.Context;
...
@POST
@Override
@Consumes(
{
"application/xml", "application/json"
})
public void create(Worldattribute entity, @Context SecurityContext sc)
{
itsLog.info("create");
final String name = sc.getUserPrincipal().getName();
FAQ
- I get WEB9102: Web Login Failed: com.sun.enterprise.security.auth.login.common.LoginException: Login failed: No LoginModules configured for flexibleJdbcRealm?
- You forgot to add the entry to the login.conf as specified above.
References
- [1] Oracle - Security in the Java EE Platform
- http://docs.oracle.com/javaee/7/tutorial/doc/security-intro.htm
- [2] The Java EE 7 Tutorial
- Release 7 for Java EE Platform
- [3] Oracle - Using the JDBC Realm for User Authentication
- http://docs.oracle.com/javaee/6/tutorial/doc/glxgo.html
- [4] Understanding Web Security
- http://java.dzone.com/articles/understanding-web-security
- [5] FlexibleJDBCRealm
- http://flexiblejdbcrealm.wamblee.org/site/
- [6] Installation instructions
- http://flexiblejdbcrealm.wamblee.org/site/documentation/snapshot/installation.html
excelente tutotrial
ReplyDelete