> On May 13, 2019, at 11:47 AM, David Blevins <[email protected]> wrote:
>
> - TOMEE-2517: MP-JWT and BeanValidation adds a fancy new feature that allows
> users to use Bean Validation to check JWTs. You simply write a validation
> constraints for against the JsonWebToken and annotate your method. A method
> no longer needs to use @RolesAllowed and can be very expressive and specific
> through the power of bean validation.
>
> - TOMEE-2517: MP-JWT and BeanValidation Example. Any new feature needs
> documentation or it doesn't exist. The example is functional and clean. The
> README is barely there and will need more work.
Ok, TOMEE-2517 has been rewritten and merged. All tests passed on my machine,
we'll see what the CI says.
The heart of this feature is that it lets you replace uses of `@RolesAllowed`
with annotations of your own creation. Simply create Bean Validation
annotations that validate against the MicroProfile JWT `JsonWebToken` interface.
A short example, where you previously had this:
@Path("/movies")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@RequestScoped
public class MovieService {
private Map<Integer, Movie> store = new ConcurrentHashMap<>();
@GET
@RolesAllowed({"manager", "user"})
public List<Movie> getAllMovies() {
return new ArrayList<>(store.values());
}
@POST
@RolesAllowed("manager")
public void addMovie(Movie newMovie) {
store.put(newMovie.getId(), newMovie);
}
}
You can now do:
@Path("/movies")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@RequestScoped
public class MovieService {
private Map<Integer, Movie> store = new ConcurrentHashMap<>();
@GET
@User
@Manager
public List<Movie> getAllMovies() {
return new ArrayList<>(store.values());
}
@POST
@Manager
public void addMovie(Movie newMovie) {
store.put(newMovie.getId(), newMovie);
}
}
Of course, you can now start to validate other parts of the JWT besides
`groups`, such as `aud` or `iss`:
@Path("/movies")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@RequestScoped
public class MovieService {
private Map<Integer, Movie> store = new ConcurrentHashMap<>();
@GET
@Issuer("https://movies.example.com")
@User
@Manager
public List<Movie> getAllMovies() {
return new ArrayList<>(store.values());
}
@POST
@Audience("movies")
@Manager
public void addMovie(Movie newMovie) {
store.put(newMovie.getId(), newMovie);
}
}
To make this work under the covers, we have to basically split a developer's
bean class into two parts which are used by the different code that needs to
validate.
So if you have a class called Foo that uses Bean Validation constraints for
both JWT and Return value, you will get the following two classes generated:
- `Foo$$ReturnConstraints` used by an updated version of `BValInterceptor`,
which is a CDI interceptor for return and parameter validation
- `Foo$$JwtConstraints` used by `ValidationInterceptor`, which is a JAX-RS
`ContainerRequestFilter` that does the JWT validation
To determine if we need to do any of this for a particular class, we have
`ClassValidationData`. If it looks as there are JWT constraints on the class,
we use `ClassValidationGenerator` to handle the generation, which in turn
delegates to these classes to do the ASM work:
- `JwtValidationGenerator`
- `ReturnValidationGenerator`
Effectively we're sorting your Bean Validation annotations into groups based on
what they validate.
Overall, I consider this feature a pre-standard prototype. The goal is to:
- show it to as many people as we can
- get people to see the appeal
- get everyone thinking about what missing pieces might have to be created if
we were to do it cleanly in a spec
There would likely need to be both Bean Validation and MicroProfile JWT
specification updates for this.
What the Bean Validation spec improvements might be is an open question. There
are a few ways we could approach it.
-David