Reputation: 214
I want to validate a user making a request. For that I have two filters: AuthenticationFilter and AuthorizationFilter. AuthenticationFilter extracts a token from the request and finds the user from the database. AuthorizationFilter checks if that user (retrieved by the previous filter) has the necessary permissions. I have two possible solutions and would like to know the pros and cons of each one and which should I use. I would also need to access the user in the actual business logic. My code is as follows:
AuthenticationFilter:
@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
// option 1.1
@Inject
@AuthenticatedUser
private Event<User> authenticatedUserEvent;
// option 1.2
@Inject
@AuthenticatedUser
private User authenticatedUser;
public void filter(ContainerRequestContext requestContext) throws IOException {
String token = getToken(requestContext);
User user = getUser(token)
if (user == null) {
requestContext.abortWith(...);
} else {
// option 1.1
authenticatedUserEvent.fire(User);
// option 1.2
authenticatedUser.setData(user);
// option 2
requestContext.setProperty("authenticatedUser", user);
}
}
}
AuthorizationFilter:
@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {
@Context
private ResourceInfo resourceInfo;
// option 1
@Inject
@AuthenticatedUser
private User authenticatedUser;
public void filter(ContainerRequestContext requestContext) throws IOException {
// option 2
User authenticatedUser = (User) requestContext.getProperty("authenticatedUser")
boolean allowed = verifyRoles(user, resourceInfo.getResourceClass(), resourceInfo.getResourceMethod());
if (!allowed) {
requestContext.abortWith(...);
}
}
}
AuthenticatedUserProducer (only for option 1):
@RequestScoped
public class AuthenticatedUserProducer {
@Produces
@RequestScoped
@AuthenticatedUser
private User authenticatedUser;
public void handleAuthenticationEvent(@Observes @AuthenticatedUser User user) {
this.authenticatedUser = user
}
}
For option 1, is it necessary to annotate the filters with @RequestScoped
? By default filters are application scoped, so is the injection safe? I.e., the event fired by filter 1 in request chain 1 will not end up injected in filter 2 of chain 2, if more than one request is being processed at the same time? And the same when injecting the user in the resource class where the actual business logic runs?
For option 2, I no longer have access to ContainerRequestContext
, but I can access the object by injecting the HttpServletRequest
. But this option seems to me "less clean" because I have to cast the stored objects to User before using them, when with the injection approach I can use the objects directly.
I already checked these questions, what I'm looking for is to determine which option is the best:
I'm currently using WildFly 11, with its default implementation of JAX-RS (Resteasy) and CDI (Weld).
Upvotes: 4
Views: 1579
Reputation: 131067
Once you are performing authentication/authorization, you could use SecurityContext
, which is part of the JAX-RS API. See an example of how to use it in your authentication filter:
final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {
@Override
public Principal getUserPrincipal() {
// Return a Principal instance according to your needs
return () -> username;
}
@Override
public boolean isUserInRole(String role) {
return true;
}
@Override
public boolean isSecure() {
return currentSecurityContext.isSecure();
}
@Override
public String getAuthenticationScheme() {
// Return the authentication scheme used by your application
return SecurityContext.BASIC_AUTH;
}
});
Then inject the SecurityContext
in your JAX-RS resource and providers with the @Context
annotation:
@Context
SecurityContext securityContext;
Then you can get the Principal
instance which was set in the SecurityContext
:
Principal principal = securityContext.getUserPrincipal();
Upvotes: 2