Reputation: 588
This seems to be a hot topic based on the amount of questions asked but I have not found the answer I am looking for just yet. I want to implement a simple authorization service in my Quarkus app, but I seem to be repeating code over and over again.
Basically, I take in the JWT from the Authorization Http header and check if the role supplied in it is sufficient to access my endpoint:
public void someApiCall(@Context HttpHeaders headers) {
authService.validate(ApiToken.SOME_API_CALL, headers); // Throws an exception when unauthorized
//…
}
Now, I think this looks really clunky and I do not like the additional parameter that I need for every single Http endpoint. I have done some research into AOP and know how to add an interceptor which could validate the Http headers through an annotation which would be applied to my method:
@Authorize
public void someApiCall(/*…*/) { /*…*/ }
The issue is, I do not know how to pass in arguments into this annotation to specify the required role. I want something like this:
@Authorize(UserRole.SYSADMIN)
This seems pretty simple but I cannot figure it out. Below you will find the interceptor and annotation classes (Missing the required role of course):
Authorize.java
@Retention(value=RUNTIME)
@Target(value=METHOD)
public @interface Authorize {}
AuthorizeInterceptor.java
@Interceptor
@Priority(3000)
@Authorize
public class AuthorizeInterceptor {
@Inject
AuthorizationService authService;
@AroundInvoke
public void validateRole(InvokationContext ctx) {
authService.validate(ApiToken.ALL, ((RestEndpoint)ctx.getTarget()).getHttpHeaders());
}
}
RestEndpoint.java
public class RestEndpoint {
@Context
HttpHeaders headers;
public HttpHeaders getHttpHeaders() { return headers; }
}
SomeResource.java
public class SomeResource extends RestEndpoint {
@GET
@Authorize
public Object someApiCall() {
/* do code directly */
}
}
So, in conclusion, where I write @Authorize
, I want to have @Authorize(UserRole.SOME_ROLE)
.
Thanks in advance!
Upvotes: 3
Views: 5436
Reputation: 588
So, I managed to figure it out. It turns out that it isn't that hard, I just didn't know where to look.
Here are the modified classes:
Authorize.java
@InterceptorBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Authorize {
// Nonbinding is very important. It makes the interceptor fire regardless of the value
@Nonbinding ApiToken value();
}
AuthorizeInterceptor.java
@Interceptor
@Priority(3000)
@Authorize(ApiToken.NULL)
public class AuthorizeInterceptor {
/* fields */
public Object validate(InvokationContext ctx) throws Exception {
authService.validate(/* stays the same */);
return ctx.proceed();
}
}
SomeResource.java
public class SomeResource {
@GET
@Authorize(ApiToken.SOME_API_CALL)
public Object someApiCall() { /* implementation */ }
}
As Turing85 pointed out, a similar API already exists in JavaEE which implements the authorization functionality in the same way.
Upvotes: 4