Is it possible to create a Java annotations equivalent to a Spring MVC #RequestMapping with a predefined set of parameters?
For instance, something that allows me to simply use:
#PostJson("/input")
public Output myMethod(Input input) {
instead of:
#RequestMapping(value = "/input",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Output myMethod(Input input) {
UPDATE:
Let me try to dig a little deeper. AFAIK, Spring seems to be able to handle meta-annotations when scanning for beans. For instance, if I create an annotation, lets say #MyComponent, and annotate it with #Component:
#Component
public #interface MyComponent {
String value() default "";
}
Spring seems able to find beans with #MyComponent and to recognize the parameters (value in this case) in #MyComponent as if they were from #Component
#MyComponent("MyBean")
public class SomeClass {
I've tryed similar tactic with #RequestMapping
#RequestMapping(method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public #interface PostJson {
String value() default "";
}
The fixed parameters (method an produces) seems to be correct, nonetheless, the variable parameter (value) is ignored.
I am hoping that this is not a #Component specific feature and that I could use it with #RequestMapping.
Not easily, no. The #RequestMapping annotation is tied to RequestMappingHandlerMapping and RequestMappingHandlerAdapter. These are two elements of the MVC stack registered by default when you provide <mvc:annotation-driven /> or #EnabledWebMvc. They only scan for #Controller and #RequestMapping, nothing else.
To make your own annotation work, you'd have to override (and register) these two or create new ones from scratch which provide the scanning and handling of handler methods. You can get some inspiration from those classes and other HandlerMapping implementations, but it really isn't a simple task.
You might, alternatively, want to look into Java Restful Web Services which can integrate quite well with Spring (not necessarily Spring MVC though). It can provide some less bloated mapping annotations when you know exactly what you want.
While this isn't currently supported out of the box but will be (thanks for creating https://jira.spring.io/browse/SPR-12296!), it isn't hard to do. If you look in RequestMappingHandlerMapping the protected method getMappingForMethod accepts a method and returns a RequestMappingInfo populated with information from the type + method-level #RequestMapping annotations. You can however populate this RequestMappingInfo from anything, e.g. your own annotation, or some other source (external routes configuration). Just follow the example of the code there.
Related
i've tried in various ways to replicate the #PreAuthorize behaviour, so spel expression with a Method Invocation context:
-I started with configuring httpSecurity in my WebSecurityConfigurerAdapter extending class with an access string written in spel, only to figure out the context was on Filter Invocation , so I had no access on the request body(which i need);
-implementing and adding a custom HandlerInterceptor to the InterceptorRegistry, but again the endpoint arguments were not accessible;
-extending ConfigGlobalMethodSecurity to create a custom expressionHandler, but I seems it is only triggered by Method-level annotations;
Can someone explain me if what i've trying to do is simply impossible or is there a way?
I'd like to have the same evaluationContext as #PreAuthorize, so having access the method arguments(I mean the value they assume) using spel expressions and be able to configure it without having to annotate every single class or method.
EDIT
for reference these are the two annotations i'm using(and they work fine) the use i'm tring to replicate not by annotations but by configuration:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole(#privilegeManager.privilegedRoles) or (#privilegeManager.verify(#id, this.getType()))")
public #interface PathVariableRestriction {
}
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole(#privilegeManager.privilegedRoles) or #dto.getOwnerId() == #myService.getCurrentId()")
public #interface RequestBodyRestriction {
}
i'd like to authorize request not based on roles but on ids: the id of the object being subject to the crud operation provided either as #PathVariable or #RequestBody(depends on if it is a get, post, put or delete) and the id of the current user retrieved through his Authentication
This is one common use case for #PostAuthorize.
For example if you do:
#PostAuthorize("returnObject.username == authentication.name")
#GetMapping("/resource/{id}")
public MyResource getResource(String id) {
// look up
return myResource;
}
This will only return resources that belong to the currently authenticated user.
This is the recommended route.
able to configure it without having to annotate every single class or method
Alternatively, you can build your own custom authorization method security from scratch using Spring Security's underlying components.
Method Security is built on top of Spring AOP. This means that you can define your own pointcut instead of using Spring Security's annotation-based one. For example, you can do:
#EnableMethodSecurity
class SecurityConfiguration {
#Bean
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor myMethodSecurityAdvisor() {
Pointcut pointcut = myCustomPointcut();
AuthorizationManager<MethodInvocationResult> rules = myRules();
AuthorizationManagerAfterMethodInterceptor interceptor =
new AuthorizationManagerAfterMethodInterceptor(
pointcut, rules);
interceptor.setOrder(AuthorizationInterceptorsOrder.
POST_AUTHORIZE.getOrder() + 1);
return interceptor;
}
}
I am using a custom code generator to transform a REST contract in to Java interfaces. Below find a sample of a generated resource Java interface.
#Generated(
date = "2018-01-30T11:56:25.156Z",
comments = "Specification filename: country.v1.json",
value = "GENERATOR"
)
#RequestMapping("/v1/countries")
public interface CountriesResource {
#RequestMapping(
method = RequestMethod.GET,
path = "/{id}",
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseBody
LandGetResourceModel getEntity(#PathVariable("id") String id);
}
Additionally the generator creates an #RestController implementation a that interface for Spring to create a controller bean.
#RestController
public class CountriesResourceImpl implements CountriesResource {
#Override
public CountryGetResourceModel getEntity(String id) {
return new CountryGetResourceModel();
}
}
So far everything works fine. Spring creates the RestController bean and the HTTP calls are directed correctly to the corresponding Handler method (e.g. getEntity).
The problem is that for some reason Spring is not able to resolve path variables when they are defined on the interface. All calls containing a path variable are handled but the method parameter of any path variable is null. If I then add the PathVariable annotation to the implementation class Spring is able to resolve the corresponding value.
Is there a way to tell Spring to read the PathVariable from the interface method declaration like it does for the RequestMapping annotations?
Unfortunately, seems like it is a known issue. I've found the following opened Jira on the question. Some explanation can be found from this answer. Also, in the same question I took the previous answer I see this solution. However, I haven't tried it and it seems to be heavy.
I'm using a bunch of microservices in my project. These microservices all share one common endpoint that returns an object which has been parsed through XML.
Now I'd love to have some kind of default method defined within the Annotation like this:
#RestController
public #interface ComaModule {
Class<? extends Object> clazz();
#RequestMapping("/descriptor")
default ModuleDescriptor getDescriptor() {
ModuleXmlUnmarshaller moduleXmlUnmarshaller = new ModuleXmlUnmarshaller(clazz());
Optional<ModuleDescriptor> moduleDescriptorOptional = moduleXmlUnmarshaller.findModuleDescriptor();
return moduleDescriptorOptional.orElse(null);
}
}
That does not work since I am not able to have a method definition in my annotation. So the hard stuff is that I want to keep #RequestMapping("/descriptor") for this.
In fact I want some kind of aspect for every RestController I use. I read about AOP for Spring and Proxy but thought I might be able to achieve this with Annotations.
May be you can try adding annotation processor class, where you can write the code which have in your post and achieve what your goal.
I'm trying to protect a Controller with the #PreAuthorize annotation at type level and try to override that behavior by annotating some methods with a different #PreAuthorize. The Problem is however, that Spring is evaluating the method annotation first (grants access) and is then evaluating the class annotation (denies access).
Is there any way to reverse that order? I couldn't figure it out yet.
Edit:
On the method level, I want to grant access to non-registered Users only:
#PreAuthorize("isAnonymous()")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String renderCreateEntity(ModelMap model) {
return userService.renderCreateEntity(model);
}
The standard for this Controller however, should be to allow fully authenticated users only:
#Controller
#RequestMapping(value = "/user")
#PreAuthorize("isFullyAuthenticated()")
public class UserController { [...] }
When debug-stepping through the app, I see that isAnonymous() is evaluated first and then isFullyAuthenticated() thus resulting in an grant of access right and immediately denying access again.
Thanks for all your replys.
The answer however, was something totally different :)
I put this here in case anyone else has the same problems.
I registered a custom validator in an #InitBinder annotated method. This binding method is called AFTER the method call requested on the controller. And since this binding method was not annotated with #PreAuthorize, the request was denied.
The solution was to annotate the binding method like this:
#InitBinder
#PreAuthorize("permitAll")
public void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
And then, the method calls from my OP evaluated like expected.
The problem is not that you need to change the order of grant and deny. The problem is simple that that method level annotations override the class level annotations.
PrePostAnnotationSecurityMetadataSource Java Doc:
Annotations may be specified on classes or methods, and method-specific annotations will take precedence.
The concrete implementation of this logic is done in the method findAnnotation of class PrePostAnnotationSecurityMetadataSource. (Unfortunately this method is private.)
So you can write your own MethodSecurityMetadataSource, if you have a look at the code of PrePostAnnotationSecurityMetadataSource, you will see how easy it is.
But one warning at the end: the end: difficult task is not rewriting the method, the difficult task is to "inject" the new MethodSecurityMetadataSource into the security system. I belive you can not do it with the spring security namespace configuration, so you need to replace spring security namespace by explicit bean declaration.
We are working on a Spring 3.0.5 Web MVC-based application. In our code we quite often do something like this:
#ModelAttribute(ModelKeys.SOME_BEAN)
public SomeBean newSomeBean() {
return new SomeBean();
}
I think this is not necessary. But if it really wasn't, then I wonder how has this managed to slip through so many code reviews? In my understanding, if a controller method wanted a NEW SomeBean, then annotating a parameter in that method with #ModelAttribute should be enough? Spring would then use the default constructor to new up the required bean for invoking the controller method, e.g.:
#RequestMapping(method = RequestMethod.POST)
public String doIt(
#ModelAttribute(ModelKeys.SOME_BEAN) final SomeBean bean,
final BindingResult bindingResult)
{
...
}
here, Spring would new up an instance of SomeBean and try to data-bind into it from the POSTed data, right? There's no need for the method as shown in the first code snippet? Please can you confirm this or provide me with your thoughts on this? Would I be introducing a risk if I just went ahead and removed all these methods that do nothing other than new up an empty bean?
#ModelAttribute annotation on a method does not bind the bean attributes with HTTP request parameters. This is the key difference from the same annotation on a method parameter. Such approach is useful to populate some data that does not depend on request parameters in the model, for instance, values of a comboboxes taken from dictionaries. This is especially helpful, if you have several handler methods in a controller, e.g. to view/change/delete the same type of object and you need the same set of model attributes in all of them.