Hi, long time (as always...) , this time about Acegi.
Environment
JBoss 4.0.5 (ejb3 configuration)
acegi – 1.0.3
Introduction
Spring frameworks is one of the most popular application frameworks used today. Acegi is a security solution “with a particular emphasis on applications that use Spring” (quote from Acegi's site). Acegi gives us a framework to perform Authentication and Authorization. In here I'm going to focus on Acegi's Container Adapter Authentication.
The container adapter authentication is the way Acegi integrates with the container used to host the application. Since I focus on authentication here lets assume that the only authorization used is the container authorization (using the @RolesAllowed, @RunAs, context.isCallerInRole(), and so on). Using the container adapter the container obtains the user credentials from Acegi – the adapter role is to authenticate the user and return to the container a user object which matches the container requirements (usually a Subject/Principal). The container Adapter (at least for JBoss) is actually a JAAS login module, which authenticate using Acegi providers and populates the subject in the layout which JBoss expects it to do.
In my example I have an Acegi security context which defines the users, a sample bean which is allowed to be executed only by users in a specific role, a JBoss dynamic login configuration Mbean, and a client.
Why?
Everything shown in this blog entry can be written very easily, and the configuration can be found on Acegi's documentation (here).But there is one important difference: on Acegi's documentation we are required to copy Acegi, Spring, and some other utility jars into the server's lib folder (${JBOSS_HOME}/server/{config}/lib. Which means that:
To change the version of the Spring, Acegi or any other library used by the authentication we have to restart the server
When looking for a class deployed both in the application and in the server's lib folder the class version in the application scope will be ignored (there are ways to workaround it but then you start to fight with JBoss....”JBoss makes possible for applications to share classes. JBoss 3.x does that by default. JBoss 4.0 does this for the"standard" configuration, but maintains class namespace isolation between applications for its "default" configuration. JBoss 4.0.1 reverts to the 3.x convention.” - taken from JBoss wiki)
And (back to Acegi's documentation) the security realm is define in the central login-config.xml file (${JBOSS_HOME}/server/{config}/conf/login-config.xml).
An external configuration to your application – one more file to configure during deployment
Requires server restart on change
In my sample I use JBoss' DynamicLoginConfig MBean which means that the security realm is defined during the application deployment automatically by JBoss, and the utility jars can be taken from the deployment unit (ear file) and can be easily hot swapped (BTW they can still be shared by applications – notice the scoping)
The EJB
Very simple:
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 eyallupu.ejbsample.common.Echo; @Remote(Echo.class) @Stateless(name="echo") @RolesAllowed("admins") public class EchoImpl implements Echo { @Resource SessionContext ctx; public String echo(String s) { Principal p = ctx.getCallerPrincipal(); System.out.println("Principal is: " + p.getName()); return s; } }
For our sample the only issue to notice is the use of the @RolesAllowed annotation. It means that if the application is configured to use a security manager (using the<security-domain> element in jboss.xml or jboss-web.xml) the security interceptor will verify, on each invocation of a bean method, that the principal of the current request belongs to the“admins” role.
The EAR and the SAR files
The ear file has the following layout
root | +--- ejb3 module (sample-app.jar) | +--- sar module (dynamic-login-config.sar) | | | +---META-INF | | | | | +----- jboss-service.xml | | +----- sample-login-config.xml | +--- beanRefFactory.xml | +--- acegisecurity.xml | +---META-INF | +---- application.xml +---- jboss-app.xml
The jboss-app.xml defines the security domain of the application (as mentioned above), the dynamic login module configuration is done on the SAR (JBoss'ServiceARchive) module. The SAR defines the dynamic login module and contains the Acegi configuration file. Here is the content of my jboss-service.xml:
<server><mbean code="org.jboss.security.auth.login.DynamicLoginConfig" name="sample-app:service=DynamicLoginConfig,domain=sampleAppDomain"><!-- The Attribute AuthConfig point to the login configuration file used by the dynamic login configuration loader --><attribute name="AuthConfig">META-INF/sample-login-config.xml</attribute><!-- The service which supports dynamic processing of login-config.xml configurations. --><depends optional-attribute-name="LoginConfigService"> jboss.security:service=XMLLoginConfig</depends><!-- Optionally specify the security mgr service to use when this service is stopped to flush the auth caches of the domains registered by this service. --><depends optional-attribute-name="SecurityManagerService"> jboss.security:service=JaasSecurityManager</depends></mbean></server>
For those of you who are familiar with SAR files the above file is obvious, for the others here is the idea: I define a managed bean (MBean) with the type of org.jboss.security.auth.login.DynamicLoginConfig, this one depends on other two MBeans (the <depends> element), and it has one attribute (property), the “AuthConfig” attribute, which defines which resource holds the configuration to dynamically load, in my case the sample-login-config.xml file. Here it is:
<?xml version='1.0'?><!DOCTYPE policy PUBLIC "-//JBoss//DTD JBOSS Security Config 3.0//EN" "http://www.jboss.org/j2ee/dtd/security_config.dtd"><policy><application-policy name="sampleAppDomain"><authentication><login-module code="org.acegisecurity.adapters.jboss.JbossAcegiLoginModule" flag="required"><module-option name="singletonId"> springRealm </module-option><module-option name="key">foobar</module-option><module-option name="authenticationManager">authenticationManager</module-option></login-module></authentication></application-policy></policy>
This is a standard JBoss
login configuration file which uses one login module – the
AcegiLoginModule (the only login module on this configuration). From
here down the rest of the configuration is as defined in the Acegidocumentation– the beanRefFactory.xml, and acegisecurity.xml files are both
included in the SAR. There is only one additional change – the
libraries that Acegi uses (like Spring, aopalliance,
acegi-security-jboss, etc.) are all included within the EAR file,
there is no need to deploy the libraries into the server's lib
folder. When including the utility jars in the EAR file * don't
forget* to reference the jars
using the Class-Path manifest attribute in the the SAR's manifest.
That's all, now we have an Acegi authentication, which can be dynamicallyloaded by JBoss, and doesn't require any deployment to the server's lib folder, or any changes to the master login configuration file.
Some References
Acegi documentation - http://www.acegisecurity.org/docbook/acegi.html
JBoss security -http://docs.jboss.org/jbossas/jboss4guide/r5/html/ch8.chapter.html