Spring MVC: Ensure parameter is valid, cross cutting through many controllers - java

I have a restful server using spring mvc. Many of the controllers (though not all) take a certain path parameter /fruits/{type}.
I need to centrally configure what types of fruit are acceptable. I.e., apple, banana, cherry are ok. For everything else I need to throw a 400 level http status code.
It is important that this be done quickly and elegantly. Some ways that this can be done are:
I write some kind of utility pojo and invoke it's method in every controller method that handles /fruit/{type}. Seems pretty cludgy.
A spring web request interceptor
Spring security?
AOP?
Something else?
What is the most professional, quick, elegant way to achieve this goal?

You might not get a 'best way' answer. Each 'best way' depends on your specific situation, needs, and desires. Here is 'a' way...
We do this via PropertyEditors during request Binding.
You could have one PropertyEditor per controller type and if you do not receive the correct type of 'thing' you're expecting then throw an exception from the PropertyEditor.
You can register your PropertyEditors per Controller or globally via a customized PropertyEditorRegistrar mapped to a RequestMappingHandlerAdapter.
I would then use the Spring SimpleMappingExceptionResolver to map your different exceptions to the view and error code you want to return.
BTW, I would consider using 404 vs 400, but that is just me.

Related

Apache Camel dispatch to local JaxRs resource implementations

I have a camel cxf-rs endpoint that splits the requests into two main parts.
form("cxfrs:bean:rsServer")
.when(isForward()).to("cxfrs:http://example.com")
.otherwise().process(myCustomDispatcher) // i want to get rid of this custom dispatcher
The first part is straight forwarded to a different service.
For the second part i would like to dispatch/call classes/methods that have jaxrs annotation. Currently i have a custom processor myCustomerDispatcher that duplicates the logic from the annotations and dispatches manually to classes and methods. Especially #Path and #PathParams are duplicated.
I tried:
to("bean:MyJaxRsImplemantion") this will work for beans with a single method matching the parameters in the camel exchange, but does not consider jax-rs annotations.
Serveral combinations with to("cxfrs:bean:cxfEndpoint?resourceClasses=MyJaxRsImplemantion"). it either requires a forwarding address, or acts on a new endpoint creating camel exchanges. I couldn't find a way to call the actual implementation.
The ProduceTemplates all seem to handle singular paths or situations.
Question:
How can i write a camel route that actually calls jax-rs resource methods without forwarding to a new service?
This is actually a really tough question. For one, even when you google it you don't get any straight answers. After an eternity of web surfing, it feels like it's the law of the land for camel routes to call jx-rs resource methods by forwarding to a new service.
And yes, it's almost like to("bean:MyJxRsImplemantion") is DESIGNED to ignore jax-rs annotations. So I've decided that it's impossible. Sorry.

Spring boot controller path extend another path

We have an application with path pattern like this:
/{language}/{subsystem}/blah/blah
the language and subsystem variable will do some common logic and apply to all 100+ controllers, i wanna ask instead of repeating this common logic 100 times in each controller, is there a way to define a centralized controller like
/{language}
which is to handle the language logic and another centralized controller like
and /{subsystem}
which is to handle the subsystem logic and all other controller kinda of 'extend' from these controllers ?
Hope i describe it clearly. many thanks and happy weekend:)
You could think about writing a custom Interceptor for your application. This interceptor could fetch the language and subsystem parts from your URL path and invoke your common logic in a central place.
There are a few pitfalls to this approach that you should balance carefully:
it's very implicit - people might miss that your interceptor is in place and could be in for a surprise
it'll apply for all incoming requests - if you want it to skip certain requests, you have to implement this logic within the interceptor
Personally, I'd take another approach and go for a combination of #RequestMapping and #PathVariable annotations on every controller method to capture language and subsystem and put the common logic into a helper method:
#GetMapping("/{language}/{subsystem}/something")
public String doSomething(#PathVariable Language language, #PathVariable Subsystem subsystem) {
LanguageSubsystemHelper.doSomething(language, subsystem);
// ...
}
Reduce the repetition to a minimum by providing a helper that's available for all controller methods. The benefits of this approach are:
you have granular control when to use or not use the common logic
it's explicit
you can automatically validate language and subsystem by binding the path variables to an enum

Java REST is there a way to default to a particular method if none of the paths match? (Instead of getting a 405)

I apologize if some of my terminology is off, I'm still trying to learn:
I'm using the Dropwizard framework and I have a resource class with all my various POST/GET/etc methods. They all work fine when hit from Postman, browsers, etc. If I try something that has no matching path in the resource class I get an exception with HTTP status 405 - method not allowed.
Is there a way to default to some other method where I can display troubleshooting help -- like a list of what the available APIs are or a link to some documentation? Sort of like a try catch type of logic. Not sure what the options are or if there is a best way to do this.
Any help is appreciated. Thanks!
I don't think you might want to do that. REST over HTTP is driven "mostly" by the HTTP method and the end-point the same will act upon it.
In any stack, try to avoid that since you have specific actions for specific resources...anything else should be treated as something the server didn't understand, in the same way the HTTP protocol would behave.
Being said that, just apply a wildcard, usually * to one of the methods as a fallback action. That should work on Jersey and Spring Boot (MVC) as well.

Secure Spring Boot app with custom scopes/roles

In my Jersey app I’ve used #RolesAllowed("my-scope-or-role-name") together with an implementation of how to read scopes from a request to ensure authorization. E.g. when HTTP header my-role-header had the value my-scope-or-role-name I was processing the request.
#RolesAllowed("my-scope-or-role-name")
public ResponseEntity<Object> post(#RequestBody final String plainText) {
// process request
}
As the service was behind a proxy, only this proxy could set the value of my-header, depending on the access token it got.
What is the idiomatic way to resolve this problem in Spring Boot? I also read about using #Secured annotation… But the #RolesAllowed() seems to be pretty straight forward.
Disclaimer: NOT an expert.
The preferred way seems to be by using the #PreAuthorize annotation.
This thing accepts SpEL expressions, which then allows you (assuming all the security and oauth2 stuff has been configured correctly - for example global method level security needs to be enabled) to do interesting things like:
#PreAuthorize("#oauth2.hasScope('read') and hasAuthority('USER')")
I would (humbly) also suggest that your REST endpoints be thin wrappers around service classes (that then do the actual work). AND that the security annotations then be pushed down into those service classes (or their interfaces - apparently you can annotate the interface and it still works).
That should be more secure since nothing can then get to the "job" code without proper security clearance.
I know this is an old post but as it's not yet been "answered", but here goes.
As is often the case with Spring the answer is actually more complicated than it seems, and there is more than one way to do this. (By the way, there is a fair amount of reading material on this. See https://developer.okta.com/blog/2019/06/20/spring-preauthorize and https://www.marcobehler.com/guides/spring-security for a more in-depth discussion on this topic.)
Method annotation or method security is what you're trying to do with #PreAuthorize, which is one of three annotations supported by Spring Security. OP mentioned #RolesAllowed, and #Secured is the more Spring-specific third annotation. As has been mentioned, the ability to use SPeL expressions is an advantage of using #PreAuthorize, but not the primary reason people use it. I think it is a matter of opinion whether you use one versus another annotation, but they're supposed to work the same, provided you've annotated your application to support them, namely with #EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) depending on what support you need.
In general, method annotation works fine. You just need to be aware that the method annotation will only work if you've enabled it with the flags I've just indicated. It also means your security is distributed across your controllers, which means you can view how your controllers/services are secured simply by looking at them. It does also mean, however, that authorization happens later in the Spring call sequence, so at least in theory access denials take just a tiny bit longer.
The alternative is using HttpSecurity configuration via WebSecurityConfigurerAdapter. The advantage I see here is you have all your security in one place, and you still get to use SPeL by using the .access() chain method. Because Spring handles security at this level much sooner your access denials will, in theory, happen faster. How much faster I couldn't say, maybe it doesn't matter, but sooner anyway if that matters to you. Where this doesn't work is if you have a controller that calls multiple #Service components depending on your access. In this scenario you have to use method security because authorization needs to happen right before the service method is called. Admittedly not the best design, but I could see a use case for this. Basically, if security is not determined or determinable by URI, then you have to rely on method security via these annotations.
I don't know if this answers the question, but hopefully it helps someone.

Where to validate data in a web app (using Spring)

This is a follow-on to my question Spring Web MVC - validate individual request params.
I've figured out how to invoke the Spring Validator on domain objects that have been created from my inputs and how to have that validator honor the JSR-303 annotations on my classes themselves. The part I can't figure out is where in my code to perform this validation. The obvious approach is to do it in the controller and return a different model and view if there's a validation failure.
But I also have a service layer which sometimes gets calls to create/update objects from input sources other than the web controller. So it's tempting to implement validation there, but the only obvious way I can think of to report a failure is throw an exception. I see Spring provides BindException but the Javadoc also basically says not to use it in application code.
What is the common/recommended practice here?
I think the answer is both.
Controllers are associated with views. You don't want the validation to disappear if you change view technologies.
Services should assume that no one is safe and validate all incoming parameters.
Other answers are all good, I'll just state one important rule:
Each subsystem/layer should validate its input, no matter where it comes from.
When you encapsule the validation logic inside of a ValidationService you can use it inside your controllers and services. As you want the user to interact with the input and to correct invalid information you should be able to display validation problems in your web view.
Sometimes you might have data (CommandObjects, Forms) which is not directly visible in the service layer and then the validation should be done in the controller which then passes the information into the service layer.
When you design your application you should think about the interaction between each layer. Mixinig validation logic into every layer might not be needed. Think about how data gets into your system. If controllers are your main entry point you can perfectly place it there since no data gets into your services without passing the validation.
At least you should validate inputs at the service layer, in order to guarantee correctness. Additionally you can do validations further up to get better usability, etc. if needed.

Categories

Resources