This feature of the Security module intercepts method calls and performs a security check before invocation is allowed to proceed.
The first piece of code required to use this API is a security binding annotation. This is what we will use to add security behavior to our business classes and methods.
Create the security binding annotation
@Retention(value = RUNTIME)
@Target({TYPE, METHOD})
@Documented
@SecurityBindingType
public @interface UserLoggedIn {}
Next, we must define an authorizer class to implement behavior for our
custom security binding type. This class is simply a CDI bean which
declares a method annotated @Secures
, qualified with the security binding
annotation we created in the first step.
This method has access to the InvocationContext
of the method call, so
if we need to access parameter arguments, we can do so using the given
context. Note that we may also inject other beans into the parameter
list of our authorizer method.
Create the authorizer
@ApplicationScoped
public class LoggedInAuthorizer
{
@Secures
@UserLoggedIn
public boolean doSecuredCheck(InvocationContext invocationContext, BeanManager manager, Identity identity) throws Exception
{
return identity.isLoggedIn();
}
}
We can then use our new annotation to secure business or bean methods.
This binding annotation may be placed on the entire class (securing all
methods) or on individual methods that you wish to secure.
Secure a bean method
@ApplicationScoped
public class SecuredBean1
{
@UserLoggedIn
public void doSomething(Thing thing)
{
thing.doSomething();
}
}
Next, we may access parameter values from the method invocation directly
in our authorizer bean by creating custom @SecurityParameterBinding
types; this is a simple step once we have completed the work above:
Create a parameter binding annotation
@Retention(value = RUNTIME)
@Target({PARAMETER})
@Documented
@SecurityParameterBinding
public @interface CurrentThing {
}
Now, when a secured method is invoked, we can inject actual parameter
values as arguments into our authorizer method, providing domain-level
security in our applications:
Update the authorizer to use parameter binding
@ApplicationScoped
public class CustomAuthorizer
{
@Secures
@UserLoggedIn
public boolean doSecuredCheck(InvocationContext invocationContext, BeanManager manager, Identity identity, @CurrentThing Thing thing) throws Exception
{
return thing.hasMember(identity);
}
}
Note that our business method must also be annotated.
Complete the Parameter Binding
@ApplicationScoped
public class SecuredBean1
{
@UserLoggedIn
public void doSomething(@CurrentThing Thing thing)
{
thing.doSomething();
}
}
Our method is now secured, and we are able to use given parameter values
as part of our security authorizer!
There may be cases where you may want to base your authorization logic
on the result of the secured method and do the security check after the
method invocation. Just use the same security binding type for that
case:
@ApplicationScoped
public class SecuredBean1
{
@UserLoggedIn
public Thing loadSomething()
{
return thingLoader.load();
}
}
Now you need to access the return value in the authorizer method. You
can inject it using the @SecuredReturn
annotation. Update the authorizer
to use a secured return value:
@ApplicationScoped
public class CustomAuthorizer
{
@Secures
@UserLoggedIn
public boolean doSecuredCheck(@SecuredReturn Thing thing, Identity identity) throws Exception
{
return thing.hasMember(identity);
}
Now the authorization will take place after the method invocation using
the return value of the business method.