This tutorial will demonstrate how you can configure Authentication/Authorization for your EJBs on WildFly using Elytron Security Framework.
Two core authentication factories are provided as part of Elytron security framework:
- HTTP Authentication Factory which is obviously used for Web applications performing HTTP Authentication
- SASL Authentication Factory which is used for other network protocols, including standard protocols such as LDAP, IMAP, etc., but also JBoss Remoting which is the EJB primary transport.
We will learn now how to deploy a FileSystem Realm to be used as repository for identities that will authenticate in an EJB application.
First of all, connect to the Command Line Interface of the application server.
$ ./jboss-cli.sh -c
Now let’s define the new File System Realm through the CLI as follows:
/subsystem=elytron/filesystem-realm=demoFsRealm:add(path=demofs-realm-users,relative-to=jboss.server.config.dir)
Once created the File System Realm called “demoFSRealm” we will add one Identity to it:
/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity(identity=ejbuser) /subsystem=elytron/filesystem-realm=demoFsRealm:set-password(identity=ejbuser,clear={password="password123"}) /subsystem=elytron/filesystem-realm=demoFsRealm:add-identity-attribute(identity=ejbuser,name=Roles, value=["guest","manager"])
The roles needed by the application will be collected from the “Roles” attribute:
/subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)
Done with the Realm, we will create a Security Domain which is bound to this Realm:
/subsystem="elytron"/security-domain="fsSD":add(default-realm="demoFsRealm",permission-mapper="default-permission-mapper",realms=[{realm="demoFsRealm",role-decoder="from-roles-attribute"},{realm="local"}])
As we will be using SASL as authentication mechanism, we will need a SASL Authentication Factory for it:
/subsystem="elytron"/sasl-authentication-factory="fs-application-sasl-authentication":add(mechanism-configurations=[{mechanism-name="JBOSS-LOCAL-USER",realm-mapper="local"},{mechanism-name="DIGEST-MD5",mechanism-realm-configurations=[{realm-name="demoFsRealm"}]}],sasl-server-factory="configured",security-domain="fsSD")
Done with Elytron, the last steps needed are adding a reference to Elytron Security Domain in the “other” Security Domain contained in the “ejb3” subsystem:
/subsystem=ejb3/application-security-domain=other:add(security-domain=fsSD)
Finally, as the EJB call initially lands on the HTTP Connector, we will add a reference to the SASL authentication factory we have created:
/subsystem=remoting/http-connector=http-remoting-connector:write-attribute(name=sasl-authentication-factory,value=fs-application-sasl-authentication)
Configuring the EJB Server Side
Done with the server side, we will configure our EJBs to use the “other ” Security Domain (which in turn references Elytron’s “fsSD” Security Domain:
import java.security.Principal; import javax.annotation.Resource; import javax.annotation.security.RolesAllowed; import javax.ejb.Remote; import javax.ejb.SessionContext; import javax.ejb.Stateless; import org.jboss.ejb3.annotation.SecurityDomain; @Stateless @Remote(SecuredEJBRemote.class) @RolesAllowed({ "guest" }) @SecurityDomain("other") public class SecuredEJB implements SecuredEJBRemote { // Inject the Session Context @Resource private SessionContext ctx; public String getSecurityInfo() { Principal principal = ctx.getCallerPrincipal(); return principal.toString(); } @RolesAllowed("manager") public boolean secured() { return true; } }
As you can see, two “guest” Role is required to access this class but for the “secured” method which requires a manager role.
Configuring the EJB Client side
From the EJB Client side, the only requirement is to provide a reference to the Authentication Configuration, by specifying the Users that are configured in the Realm:
<configuration> <authentication-client xmlns="urn:elytron:1.0"> <authentication-rules> <rule use-configuration="default-config"/> </authentication-rules> <authentication-configurations> <configuration name="default-config"> <set-user-name name="ejbuser"/> <credentials> <clear-password password="password123"/> </credentials> <sasl-mechanism-selector selector="DIGEST-MD5"/> <providers> <use-service-loader /> </providers> </configuration> </authentication-configurations> </authentication-client> </configuration>
The Client code is straightforward: the basic getSecurityInfo() will be available to anyone with the “guest” role, while the secured() will be available just for the “manager” role.
import java.util.Hashtable; import javax.ejb.EJBAccessException; import javax.naming.Context; import javax.naming.InitialContext; public class RemoteEJBClient { public static void main(String[] args) throws Exception { final Hashtable jndiProperties = new Hashtable(); jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory"); jndiProperties.put(Context.PROVIDER_URL, "remote+http://localhost:8080"); final Context context = new InitialContext(jndiProperties); SecuredEJBRemote reference = (SecuredEJBRemote) context.lookup("ejb:/ejb-remote-server/SecuredEJB!" + SecuredEJBRemote.class.getName()); System.out.println("Successfully called secured bean, caller principal " + reference.getSecurityInfo()); boolean hasFullPermission = false; try { hasFullPermission = reference.secured(); } catch (EJBAccessException e) { } System.out.println("\nPrincipal has admin permission: " + hasFullPermission); } }
If you configured (or if you update) the Realm to have both Roles, then you would be able to call both methods successfully:
/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity-attribute(identity=ejbuser,name=Roles, value=["guest","manager"])