I’ve been working on this for a while and ended up with my own solution. It is implemented at JAX-RS
interface level (as suggested by Pradeep Pati) – so if you need to access your beans by EJB
, it won’t work.
So, as I've found here, ContainerRequestFilter
can access the resource method (or class) annotations, so all I needed to do is:
1. Implement my own @RolesAllowed
annotation:
@Inherited
@Target( {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RolesAllowed {
String[] value();
}
2. Implement ContainerRequestFilter
with custom authentication and authorization:
@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {
@Context
private ResourceInfo resourceInfo;
@Override
public void filter(ContainerRequestContext context) throws IOException {
// here we have access to headers:
String authorizationHeader = context.getHeaderString("Authorization");
// and, thanks to injected resourceInfo, to annotations:
RolesAllowed annotation = resourceInfo
.getResourceClass() // or getResourceMethod(), I've used both
.getAnnotation(RolesAllowed.class);
// and, finally, to the roles (after a null-check)
String[] roles = annotation.value();
// then you can authenticate and authorize everything on your own using any method (I’ve used Basic Auth and JWT)
// and, if something fails, you can abort the request:
if (!isAuthenticated) {
context.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
} else if (!isAuthorized) {
context.abortWith(Response.status(Response.Status.FORBIDDEN).build());
}
}
...
}
However, I’ve evaluated Avishai’s solution (to use PicketLink) too. While this is a little harder to implement and sometimes could be complicated (for example basic JPA scenario needs about 7-8 JPA entities), it would be better choice if you need solid, extensible security system with a lot of options (like LDAP or JPA, or even both at the same time) or with various authentication options (like Basic and JWT auth at the same time, but with different headers, for example). There are probably hundreds of pros and/or cons on the topic, so that's not an easy choice.
Funny thing is, PicketLink uses it’s own @org.picketlink.authorization.annotations.RolesAllowed
annotation instead of javax.annotation
one. Still, it should work well with EJB
calls because it uses EJB
interceptors, not JAX-RS
filters to check the roles.
But for me it had seemed like an overkill, so I came up with my own, not-so-sophisticated (but working) solution.