I have a JAX-RS service where I want all my users to access my services, but just those who have rights to see the result. Roles based security and existing REALMS and atuhentication methods doesn't fit my requirement.
For example:
Question is: Where should I check for users ID, in some separate filter, security context or in every REST method implementation? How to provide REST methods with this ID, can securityContext be injected in every method after filtering request by ID?
I'm using GlassFish 4.1 and Jersey JAX-RS implementation.
To authenticate a user, a client application must send a JSON Web Token (JWT) in the authorization header of the HTTP request to your backend API. API Gateway validates the token on behalf of your API, so you don't have to add any code in your API to process the authentication.
JWT-based API auth is a good choice for securing microservices within an organization, or sharing APIs with certain types of external clients. JWT tokens are typically not revokable. To revoke a JWT token you typically have to roll the secrets of that client - this will disable ALL JWT tokens currently issued.
JSON Web Token (JWT) provides a mechanism for sharing a set of claims or properties from a client to a microservices application in an encrypted and secure way. JWTs can also secure communication between services or pass end-user context and data between microservices.
Once verified, the API will create a JSON Web Token and sign it using a secret key. Then, the API will return that token back to the client application. Finally, the client app will receive the token, verify it on its own side to ensure it's authentic, and then use it on every subsequent request.
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 a SecurityContext
. 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
SecurityContext
isUserInRole
method. This will be used for authorization.User
class, that implements java.security.Principal
. I returned this custom object SecurityContext
in the ContainerRequestContext
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. Principal
and cast it to User
. So really you can create any class that implements Principal
, and use this object however you want.The use of the @RolesAllowed
annotation. With Jersey, there is a filter that checks the SecurityContext.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);
}
}
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With