I was wondering if it is possible to do the following trick with jersey restful resources:
I have an example jersey resource:
#Path("/example")
public class ExampleRessource {
#GET
#Path("/test")
#CustomPermissions({"foo","bar"})
public Response doStuff() {
//implicit call to checkPermissions(new String[] {"foo","bar"})
}
private void checkPermissions(String[] permissions) {
//stuff happens here
}
}
What I want to achieve is: before executing each resource's method to implicitly check the rights from the annotation by calling the checkPermissions method without actually writing the call inside the method body. Kind of "decorating" each jersey method inside this resource.
Is there an elegant solution? For example with jersey Provider?
Thx!
With Jersey 2 can use ContainerRequestFilter.
#Provider
public class CheckPermissionsRequestFilter
implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext crc) throws IOException {
}
}
We can get the annotation on the called method through the ResourceInfo class
#Context
private ResourceInfo info;
#Override
public void filter(ContainerRequestContext crc) throws IOException {
Method method = info.getResourceMethod();
CheckPermissions annotation = method.getAnnotation(CheckPermissions.class);
if (annotation != null) {
String[] permissions = annotation.value();
}
}
You can use this annotation
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckPermissions {
String[] value();
}
And annotate the resource class or the resource method with #CheckPermissions({...})
See more at Filters and Interceptors
UPDATE
The annotation above allows for annotating classes also. Just for completeness, you'll want to check the class also. Something like
Class resourceClass = info.getResourceClass();
CheckPermissions checkPermissions = resourceClass.getAnnotation(CheckPermissions.class);
if (checkPermissions != null) {
String[] persmission = checkPermissions.value();
}
Related
My RestClient is annotated by a custom annotation and I want to get the annotation value in a ClientRequestFilter.
Here is my MicroProfile RestClient:
#Path("/greetings")
#RegisterRestClient
#MyAnnotation("myValue")
public interface MyRestClient{
#GET
public String hello();
}
I want to get the annotation value in my ClientRequestFilter:
public class MyFilter implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext requestContext) {
// Here i want to get the MyAnnotation value. i.e "myValue"
}
}
I tried to call the requestContext.getClient().getAnnotations() method but it does not work since requestContext.getClient() is an instance of org.jboss.resteasy.microprofile.client.impl.MpClient
The implementation in question is RESTEasy. I would like to find a way to get this information from both RESTEasy classic and RESTEasy reactive implementations.
Thanks for your help
Here is the MicroProfile REST Client specific way:
#Provider
public class MyFilter implements ClientRequestFilter {
public void filter(final ClientRequestContext clientRequestContext) {
final Method method = (Method) clientRequestContext
.getProperty("org.eclipse.microprofile.rest.client.invokedMethod");
Class<?> declaringClass = method.getDeclaringClass();
System.out.println(declaringClass);
MyAnnotation myAnnotation = declaringClass.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.value());
}
}
which must work in all implementations including RESTEasy (Classic and Reactive) or Apache CXF.
This should work:
import org.jboss.resteasy.client.jaxrs.internal.ClientRequestContextImpl;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.ext.Provider;
#Provider
public class MyFilter implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext requestContext) {
Class<?> declaringClass = ((ClientRequestContextImpl) requestContext)
.getInvocation()
.getClientInvoker()
.getDeclaring();
MyAnnotation myAnnotation = declaringClass.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.value());
}
}
Just to mention, this is really RESTEasy specific. The class ClientRequestContextImpl comes from the internal RESTEasy package and thus might be subject to change.
I'm currently making a RESTapi using jersey 2.27 and jetty 9.4.
In this server I'm trying to apply a filter:
#AuthenticationEndpoint.Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
private static final String REALM = "example";
private static final String AUTHENTICATION_SCHEME = "Bearer";
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
//Authentication code
}
private boolean isTokenBasedAuthentication(String authorizationHeader) {
}
private void abortWithUnauthorized(ContainerRequestContext requestContext) {
}
private void validateToken(String token) throws Exception {
}
}
However, this filter isn't triggered.
This is my endpoint:
#Path("/authenticate")
public class AuthenticationEndpoint {
Machine machine = Machine.getInstance();
#NameBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Secured { }
#POST
#Path("/authenticate")
#Secured
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response authenticateUser(
AuthenticationRequest authenticationRequest){
}
I don't have a web.xml and I wouldn't know how to actually get this filter to trigger. Anyone have some advice for this? I'm having a hard time understanding this server filter configuration.
PS: i left out the content of the methods since I thought it would be too chaotic, I will of course add it if it is deemed necessary.
You have to register the filter when you create the Application, something like
public class MyApplication extends ResourceConfig {
register(AuthenticationFilter.class)
// yada yada
}
I see that with Spring boot is really simple create filters. Just follow post like this one https://www.baeldung.com/spring-boot-add-filter
What I have not able to find, is how to create annotations that subscribe specifics endpoints in the controller to one filter.
Something like in Jax-RS it would looks like
#GET
#Path("jax-rs-single")
#Reactive(ttlRequest = 2000)
#Produces(MediaType.APPLICATION_JSON)
public Single getSingle() {
return Single.just("Hello world single");
}
Where #Reactive it would trigger the ReactiveFilter implementation per request.
I also saw the #WebFlow annotation, but it's not what I want. I want to create a library where the consumers decide which filter use, just adding the annotation in the controller.
Any idea how to do something similar with Spring boot/MVC ?
Regards
I will try to describe here more about Custom annotation and the processor in Spring.
I don't know what you want or what you need, but I will give an generic example.
You have 2 options:
BeanProcessor
HandlerInterceptor
BeanProcessor
You need to build 3 things basically: Annotaton, BeanProcessor and a Callback to execute your logic if annotated. Here is an example of it and how it works:
1 - Create the annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Documented
public #interface Reactive {
Integer ttlRequest;
}
2 - Implement a BeanPostProcessor
#Component
public class ReactiveAnnotationProcessor implements BeanPostProcessor {
private ConfigurableListableBeanFactory configurableBeanFactory;
#Autowired
public ReactiveAnnotationProcessor(ConfigurableListableBeanFactory beanFactory) {
this.configurableBeanFactory = beanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
this.scanReactiveAnnotation(bean, beanName);
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
protected void scanReactiveAnnotation(Object bean, String beanName) {
this.configureMethodInjection(bean);
}
private void configureMethodInjection(Object bean) {
Class<?> managedBeanClass = bean.getClass();
MethodCallback methodCallback =
new ReactiveMethodCallback(configurableBeanFactory, bean);
ReflectionUtils.doWithMethod(managedBeanClass, methodCallback);
}
}
3 - Create the method callback (here is the logic to execute)
public ReactiveMethodCallback implements MethodCallback {
private ConfigurableListableBeanFactory configurableBeanFactory;
private Object bean;
public ReactiveMethodCallback(ConfigurableListableBeanFactory bf, Object bean) {
configurableBeanFactory = bf;
this.bean = bean;
}
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if (!method.isAnnotationPresent(Reactive.class)){
return;
}
//YOUR LOGIC HERE
}
}
Here is a good source about annotation processing, it is about FieldProcessing but you can just change the interfaces to implement what you need if you have doubts: https://www.baeldung.com/spring-annotation-bean-pre-processor
[UPDATED] You can also create a HandlerInterceptor instead:
HandlerInterceptor
public class ReactiveFilterHandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Test if the controller-method is annotated with #CustomFilter
Reactive filter = handlerMethod.getMethod().getAnnotation(Reactive.class);
if (filter != null) {
// ... do the filtering, or call the Component for filtering
}
}
return true;
}
}
And register your handler:
#Configuration
public class WebMvcConfig extends WebMvcConfigurer {
#Autowired
ReactiveFilterHandlerInterceptor reactiveFilterHandlerInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(reactiveFilterHandlerInterceptor);
}
}
If I understand what you want correctly the main problem is how to apply filter based on custom annotation.
So first of all, yes you can use a regular Spring filter (WebFilter in case of Spring Webflux or Filter in case of Spring MVC), but you'll need to write some custom logic.
To do filtering based on annotation you should:
Use RequestMappingHandlerMapping#getHandlerInternal() method to retrieve a reference to the method that handles the request (the getSingle() in your case)
When you manage to retrieve the HandlerMethod then you can check if that method has your custom annotation applied with hasMethodAnnotation(Class<A> annotationType) method.
When you know that, then you can react accordingly: either chain.doFilter(request, response) without performing any actions, or apply your custom logic, and then trigger the rest of the filter chain.
I have a registration endpoint that I only want anonymous users to be able to access. In other words, I only want non-authenticated users to be able to POST to the endpoint. What is the best way to go about doing this?
#Path("/accounts")
public class AccountResource {
#Inject
private AccountService accountService;
#DenyAll
#POST
public void register(CreateAccountJson account) {
try {
accountService.registerUserAndCreateAccount(account.getEmail(),
account.getPassword());
} catch (RegistrationException e) {
throw new BadRequestException(e.getMessage());
}
}
}
There's no such annotation. This use case doesn't really fit into the semantics of authorization. One work around you can use is to inject the SecurityContext. Just check if there is a Principal. If not, then there is no authenticated user. If there is, then you could just send a 404
#POST
public void register(#Context SecurityContext context, CreateAccountJson account) {
if (context.getUserPrincipal() != null) {
throw new NotFoundException();
}
...
}
UPDATE
If you have a lot of resource methods like this, it might be better to use a filter that is name bound. For example
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface NonAuthenticated {}
#NonAuthenticated
// Perform before normal authorization filter
#Priority(Priorities.AUTHORIZATION - 1)
public class NonAuthenticatedCheckFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext request) {
final SerurityContext context = request.getSecurityContext();
if (context.getUserPrincipal() != null) {
throw new ForbiddenException();
}
}
}
#POST
#NonAuthenticated
public void register(CreateAccountJson account) { }
// register the Dw
environment.jersey().register(NonAuthenticatedCheckFilter.class);
For more information on Jersey filters see Filter and Interceptors from Jersey docs.
Assume I have a following code in Java EE / EJB / JAX-RS:
#POST
#Path("some/path")
#MyAnnotation
public MyResponse createActivation(MyRequest request, CustomValue value) {
// ...
}
How do I check for the presence of custom #MyAnnotation annotation and populate CustomValue value method parameter based on some request context parameters in case the annotation is present?
Note: I already have this code in Spring using HandlerInterceptorAdapter and HandlerMethodArgumentResolver. Now I need to do the same without Spring. I have already discovered the ContainerRequestFilter and I use it to check for the annotation, but now I am struggling with injecting the method parameter.
Custom method parameter injection is handled a little differently from normal (i.e. field, constructor) injection. With Jersey, this requires the implementation of a ValueFactoryProvider. For your case it would look something like
public class MyAnnotationParamValueProvider implements ValueFactoryProvider {
#Inject
private ServiceLocator locator;
#Override
public Factory<?> getValueFactory(Parameter parameter) {
if (parameter.getAnnotation(MyAnnotation.class) != null
&& parameter.getRawType() == CustomValue.class) {
final Factory<CustomValue> factory
= new AbstractContainerRequestValueFactory<CustomValue>() {
#Override
public CustomValue provide() {
final ContainerRequest request = getContainerRequest();
final String value = request.getHeaderString("X-Value");
return new CustomValue(value);
}
};
locator.inject(factory);
return factory;
}
return null;
}
#Override
public PriorityType getPriority() {
return Priority.NORMAL;
}
}
Then you need to register it with the ResourceConfig
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bind(MyAnnotationParamValueProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
}
});
}
}
See a complete example in this Gist
See also:
Custom Method Parameter Injection with Jersey. It shows another way to do this, where you don't need to explicitly inject, and also you will be able to inject the value in all three areas (field, constructor, and method param).