Custom JAX-RS authorization - using JWT in each request
Solution 1:
You can perform this logic in a ContainerRequestFilter
. It pretty common to handle custom security features in here.
Some things to consider
The class should be annotated with
@Priority(Priorities.AUTHENTICATION)
so it is performed before other filters, if any.You should make use of the
SecurityContext
, inside the filter. What I do is implement aSecurityContext
. You can really implement it anyway you want.
Here's a simple example without any of the security logic
@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
SecurityContext originalContext = requestContext.getSecurityContext();
Set<String> roles = new HashSet<>();
roles.add("ADMIN");
Authorizer authorizer = new Authorizer(roles, "admin",
originalContext.isSecure());
requestContext.setSecurityContext(authorizer);
}
public static class Authorizer implements SecurityContext {
Set<String> roles;
String username;
boolean isSecure;
public Authorizer(Set<String> roles, final String username,
boolean isSecure) {
this.roles = roles;
this.username = username;
this.isSecure = isSecure;
}
@Override
public Principal getUserPrincipal() {
return new User(username);
}
@Override
public boolean isUserInRole(String role) {
return roles.contains(role);
}
@Override
public boolean isSecure() {
return isSecure;
}
@Override
public String getAuthenticationScheme() {
return "Your Scheme";
}
}
public static class User implements Principal {
String name;
public User(String name) {
this.name = name;
}
@Override
public String getName() { return name; }
}
}
A few things to notice
- I've created a
SecurityContext
- I've added some roles, and used them for the
isUserInRole
method. This will be used for authorization. - I've created a custom
User
class, that implementsjava.security.Principal
. I returned this custom object - Finally I set the new
SecurityContext
in theContainerRequestContext
Now what? Let's look at a simple resource class
@Path("secure")
public class SecuredResource {
@GET
@RolesAllowed({"ADMIN"})
public String getUsername(@Context SecurityContext securityContext) {
User user = (User)securityContext.getUserPrincipal();
return user.getName();
}
}
A few things to notice:
-
SecurityContext
is injected into the method. - We get the
Principal
and cast it toUser
. So really you can create any class that implementsPrincipal
, and use this object however you want. -
The use of the
@RolesAllowed
annotation. With Jersey, there is a filter that checks theSecurityContext.isUserInRole
by passing in each value in the@RolesAllowed
annotation to see if the User is allowed to access the resource.To enable this feature with Jersey, we need to register the
RolesAllowedDynamicFeature
@ApplicationPath("/api") public class AppConfig extends ResourceConfig { public AppConfig() { packages("packages.to.scan"); register(RolesAllowedDynamicFeature.class); } }
Solution 2:
I was searching for an solution which is Jersey independent and works for Wildfly -> found this github example implementation:
https://github.com/sixturtle/examples/tree/master/jaxrs-jwt-filter
It should give you a hint how to solve it clean.
Implement a JWTRequestFilter which implements ContainerRequestFilter https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java
as stated above and register the filter as resteasy provider in web.xml:
<context-param>
<description>Custom JAX-RS Providers</description>
<param-name>resteasy.providers</param-name>
<param-value>com.sixturtle.jwt.JWTRequestFilter</param-value>
</context-param>
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>