I've been asked in the official Spring forums about one of my previous posts. In particular, a Senior Member there, wanted to know a little more about my impressions with Acegi and why did I assert that Acegi has a steep learning curve. I'm going to try to answer that question here and, by the way, provide some sample code too.
Goes without saying that Acegi features are impressive and difficult if not impossible to find in other software packages. And, of course, is Open Source to boot. It offers an endless list of capabilities all of them related to securing your applications. So then...what's not to love?
Let's talk about me a little. I currently work as a Software Architect at one of the biggest companies in Spain. I've worked before for companies like IBM or Accenture. I've worked with several products and technologies but mainly Java. Saying Java in this kind of companies is the same as saying J2EE (JEE if you like it).
I'm in a project now. A project whose security features could be considered pretty standard (let's say it's not a bank). We need to secure our applications (authentication) and (dis)allow access to features based in the authenticated user permissions (authorization). Nothing really amazing here but my guess is that the gross of the projects are like this. As we are using an Application Server (Weblogic 10 will be) we have all kind of services already provided by the container. As a matter of fact I only miss two things:
Chapter 20 of the Reference Guide shows the following code
Goes without saying that Acegi features are impressive and difficult if not impossible to find in other software packages. And, of course, is Open Source to boot. It offers an endless list of capabilities all of them related to securing your applications. So then...what's not to love?
Let's talk about me a little. I currently work as a Software Architect at one of the biggest companies in Spain. I've worked before for companies like IBM or Accenture. I've worked with several products and technologies but mainly Java. Saying Java in this kind of companies is the same as saying J2EE (JEE if you like it).
I'm in a project now. A project whose security features could be considered pretty standard (let's say it's not a bank). We need to secure our applications (authentication) and (dis)allow access to features based in the authenticated user permissions (authorization). Nothing really amazing here but my guess is that the gross of the projects are like this. As we are using an Application Server (Weblogic 10 will be) we have all kind of services already provided by the container. As a matter of fact I only miss two things:
- Be able to secure Spring beans (as they are not managed by the container, see it)
- Hide/Show widgets in a JSP depending on the user roles
Chapter 20 of the Reference Guide shows the following code
<authz:authorize ifAllGranted="ROLE_SUPERVISOR">
<td>
<A HREF="del.htm?id=<c:out value="${contact.id}"/>">Del</A>
</td>
</authz:authorize>
<td>
<A HREF="del.htm?id=<c:out value="${contact.id}"/>">Del</A>
</td>
</authz:authorize>
And Chapter 21 has the following:
public interface BankManager {
/**
Secure Object Implementations
Acegi Security System for Spring 77
* Delete something
*/
@Secured({"ROLE_SUPERVISOR","RUN_AS_SERVER" })
public void deleteSomething(int id);
...
}
/**
Secure Object Implementations
Acegi Security System for Spring 77
* Delete something
*/
@Secured({"ROLE_SUPERVISOR","RUN_AS_SERVER" })
public void deleteSomething(int id);
...
}
So far so good, isn't it?. In less than an hour I know what I should use! Nothing more nothing less..or not?
Well, may be, it won't be that simple. To start using this niceties you have to take some steps first. First there are the configuration issues, second the Authorities. A quick read tells me I will probably have to plug the authentication module aswell. And forget about roles, everything here is based on the concept of Authority.
I don't go on reading now. I don't want to have multiple concepts for the same purpose. It seems I would need to switch everything to Acegi and integrate it then with Weblogic (EJB3). That seems a lot of work for a simple tag.
I'm not saying Acegi is too complex (it is but just because it solves complex problems aswell). I'm not saying it's not worth the effort (it is if you need a catch all solution). But it was complex for my needs this time. You could argue that we are already using a pretty expensive AS but then, they always seem to be available, that is, 99% of my projects are somehow web related and executed in a container (even though I understand the importance Java outside a container).
Nonetheless, I learned a couple of important things while considering Acegi as a possible solution and I would like to share at least one of them. After taking a peek to the source code I decided to create a more simple authorize tag that suits my problem (it somehow mimics Acegi behavior with JEE roles). Here are the results:
Well, may be, it won't be that simple. To start using this niceties you have to take some steps first. First there are the configuration issues, second the Authorities. A quick read tells me I will probably have to plug the authentication module aswell. And forget about roles, everything here is based on the concept of Authority.
I don't go on reading now. I don't want to have multiple concepts for the same purpose. It seems I would need to switch everything to Acegi and integrate it then with Weblogic (EJB3). That seems a lot of work for a simple tag.
I'm not saying Acegi is too complex (it is but just because it solves complex problems aswell). I'm not saying it's not worth the effort (it is if you need a catch all solution). But it was complex for my needs this time. You could argue that we are already using a pretty expensive AS but then, they always seem to be available, that is, 99% of my projects are somehow web related and executed in a container (even though I understand the importance Java outside a container).
Nonetheless, I learned a couple of important things while considering Acegi as a possible solution and I would like to share at least one of them. After taking a peek to the source code I decided to create a more simple authorize tag that suits my problem (it somehow mimics Acegi behavior with JEE roles). Here are the results:
public class Authorize extends TagSupport {
private Log log = LogFactory.getLog(Authorize.class);
private String ifAllGranted = null;
private String ifAnyGranted = null;
private String ifNotGranted = null;
public void setIfAllGranted(String ifAllGranted) {
this.ifAllGranted = ifAllGranted;
}
public void setIfAnyGranted(String ifAnyGranted) {
this.ifAnyGranted = ifAnyGranted;
}
public void setIfNotGranted(String ifNotGranted) {
this.ifNotGranted = ifNotGranted;
}
private Set parse(String roles) {
Set setOfRoles = new HashSet();
if (roles != null) setOfRoles.addAll(Arrays.asList(roles.split(",")));
return setOfRoles;
}
private boolean inAny(HttpServletRequest request, Set roles) {
boolean complies = false;
for (String role : roles) complies |= request.isUserInRole(role);
return complies;
}
private boolean inAll(HttpServletRequest request, Set roles) {
boolean complies = true;
for (String role : roles) complies &= request.isUserInRole(role);
return complies;
}
public int doStartTag() throws JspException {
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
Set notGranted = parse(
ExpressionEvaluationUtils.evaluateString(
"ifNotGranted", ifNotGranted, pageContext));
if ((notGranted.size() > 0) && (inAny(request, notGranted)))
return Tag.SKIP_BODY;
Set anyGranted = parse(
ExpressionEvaluationUtils.evaluateString(
"ifAnyGranted", ifAnyGranted, pageContext));
if ((anyGranted.size() > 0) && (inAny(request, anyGranted)))
return Tag.EVAL_BODY_INCLUDE;
Set allGranted = parse(
ExpressionEvaluationUtils.evaluateString(
"ifAllGranted", ifAllGranted, pageContext));
if ((allGranted.size() > 0) && (!inAll(request, allGranted)))
return Tag.SKIP_BODY;
if (anyGranted.size() > 0) return Tag.SKIP_BODY;
return Tag.EVAL_BODY_INCLUDE;
}
}
private Log log = LogFactory.getLog(Authorize.class);
private String ifAllGranted = null;
private String ifAnyGranted = null;
private String ifNotGranted = null;
public void setIfAllGranted(String ifAllGranted) {
this.ifAllGranted = ifAllGranted;
}
public void setIfAnyGranted(String ifAnyGranted) {
this.ifAnyGranted = ifAnyGranted;
}
public void setIfNotGranted(String ifNotGranted) {
this.ifNotGranted = ifNotGranted;
}
private Set
Set
if (roles != null) setOfRoles.addAll(Arrays.asList(roles.split(",")));
return setOfRoles;
}
private boolean inAny(HttpServletRequest request, Set
boolean complies = false;
for (String role : roles) complies |= request.isUserInRole(role);
return complies;
}
private boolean inAll(HttpServletRequest request, Set
boolean complies = true;
for (String role : roles) complies &= request.isUserInRole(role);
return complies;
}
public int doStartTag() throws JspException {
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
Set
ExpressionEvaluationUtils.evaluateString(
"ifNotGranted", ifNotGranted, pageContext));
if ((notGranted.size() > 0) && (inAny(request, notGranted)))
return Tag.SKIP_BODY;
Set
ExpressionEvaluationUtils.evaluateString(
"ifAnyGranted", ifAnyGranted, pageContext));
if ((anyGranted.size() > 0) && (inAny(request, anyGranted)))
return Tag.EVAL_BODY_INCLUDE;
Set
ExpressionEvaluationUtils.evaluateString(
"ifAllGranted", ifAllGranted, pageContext));
if ((allGranted.size() > 0) && (!inAll(request, allGranted)))
return Tag.SKIP_BODY;
if (anyGranted.size() > 0) return Tag.SKIP_BODY;
return Tag.EVAL_BODY_INCLUDE;
}
}
Remember to create a taglib aswell
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>security</short-name>
<uri>/WEB-INF/security</uri>
<tag>
<name>authorize</name>
<tag-class>es.internna.spring.Authorize</tag-class>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>ifNotGranted</name>
<required>false</required>
</attribute>
<attribute>
<name>ifAnyGranted</name>
<required>false</required>
</attribute>
<attribute>
<name>ifAllGranted</name>
<required>false</required>
</attribute>
</tag>
</taglib>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>security</short-name>
<uri>/WEB-INF/security</uri>
<tag>
<name>authorize</name>
<tag-class>es.internna.spring.Authorize</tag-class>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>ifNotGranted</name>
<required>false</required>
</attribute>
<attribute>
<name>ifAnyGranted</name>
<required>false</required>
</attribute>
<attribute>
<name>ifAllGranted</name>
<required>false</required>
</attribute>
</tag>
</taglib>
And reference it in your pages
<%@ taglib uri="WEB-INF/lib/security.jar" prefix="security" %>
But...everything I've written before could be biased and false (ignorance is a bliss) so, if you know more and can point me in the right direction and prove me wrong, please do it!! Till next time
