-
Simple security interceptor in Weld/JSF2
Posted on March 31st, 2010 7 commentsWaiting for the Seam3 security module, I wrote a simple security interceptor (inspired by the Seam2 security annotation). You can use it like this:
@Secure("#{loggedInUser.name == arg0.name}") public List<Message> listMessages(User owner) { ... }Here,
loggedInUseris a@NamedWeld (CDI) bean, andarg0is the first argument of the method. This is a slight modification to the Seam2 security annotation, where it wasn’t possible to reference arguments. If the EL expression doesn’t evaluate totrue, an exception is thrown.The implementation is pretty straightforward, as adding an interceptor in Weld is really simple. First, we need the annotation:
/** * @author Adam Warski (adam at warski dot org) */ @Retention(RUNTIME) @Target({TYPE, METHOD}) @InterceptorBinding public @interface Secure { /** * @return The EL expression that should be evaluated. If it evaluates to * {@code true}, access will be granted. The EL expression may reference * any objects that are in any context, as well as the arguments of the method, * under the names {@code arg0, arg1, arg2, ...}. */ @Nonbinding String value(); }The key components here is the
@InterceptorBindingmeta-annotation, and specifying the value of to be@Nonbinding. If the value element was binding, then we would need to define an interceptor for each possible String value.Next, the interceptor itself:
@Interceptor @Secure("") public class SecurityInterceptor { private String getArgName(int index) { return "arg" + index; } @AroundInvoke public Object invoke(InvocationContext ctx) throws Exception { FacesContext facesCtx = FacesContext.getCurrentInstance(); ELContext elCtx = facesCtx.getELContext(); Secure secure = getSecureAnnotation(ctx.getMethod()); String expression = secure.value(); // Populating the request map so that parameters are available (arg0, ...) Map<String, Object> requestMap = facesCtx.getExternalContext() .getRequestMap(); for (int i = 0; i < ctx.getParameters().length; i++) { Object parameter = ctx.getParameters()[i]; requestMap.put(getArgName(i), parameter); } Boolean expressionValue = (Boolean) facesCtx.getApplication() .getExpressionFactory() .createValueExpression(elCtx, expression, Boolean.class) .getValue(elCtx); // Removing the parameters (arg0, arg1, ...) for (int i = 0; i < ctx.getParameters().length; i++) { requestMap.remove(getArgName(i)); } if (expressionValue == null || !expressionValue) { throw new SecurityException(); } return ctx.proceed(); } private Secure getSecureAnnotation(Method m) { for (Annotation a: m.getAnnotations()) { if (a instanceof Secure) { return (Secure) a; } } for (Annotation a: m.getDeclaringClass().getAnnotations()) { if (a instanceof Secure) { return (Secure) a; } } throw new RuntimeException("@Secure not found on method " + m.getName() + " or its class " + m.getClass().getName()); } }And finally, we need an entry in
beans.xml, enabling the interceptor:<interceptors> <class>util.security.SecurityInterceptor</class> </interceptors>
One shortcoming is that it’s not currently possible to place one
@Secureannotation on the class, and another on the methods (see this thread on the forum). The idea is that then the class-level annotation expresses general security constraints, which can be later refined on the method level.Another missing feature, which could be easily added, is a message parameter to the annotation, which would be included in the exception in case the check fails.
Adam
5 responses to “Simple security interceptor in Weld/JSF2”

-
Hey Adam,
Great post! It nicely demonstrates how simple it is to implement powerful features from Seam 2 in just a couple lines of code.
I have two suggestions:
Rather than referencing arg0, arg1, etc in the EL expression, it would perhaps be cleaner and clearer to use annotations on designated method arguments to alias them to EL variables. Something like:
@Secure(“#{loggedInUser.name == owner.name}”)
public List listMessages(@ELVar(“owner”) User owner) { … }That brings me to my next suggestion. Rather than putting the temporary EL variables in request scope, the more formal way of doing it is to register them with the VariableMapper. That is how the iteration variables work in the c:forEach tag, for example:
See ELContext#getVariableMapper()
Cheers!
Dan
-
Thanks for the interesting article!
-
Joselito Sem Noção April 14th, 2010 at 23:22
Hi!
It’s possible to put a SQL query inside annotation?
I like it! I will put all my code inside annotation… doing this I will not have any compilation errors!!
Thanks for the brilliant idea!!
2 Trackbacks / Pingbacks
-
[...] my previous post, I described how to create a simple security interceptor, which checks conditions defined [...]
-
[...] Stackable Security Interceptors, about which I blogged here and here. Example [...]
Leave a reply
-

Dan Allen April 1st, 2010 at 23:01