How concatenate two string in Spring Expression Language (SpEL) - spring

In my spring application, the methods from my controller and service classes have this annotation to security purposes:
#PreAuthorize("hasPermission(#user, 'cadastra')")
the second argument, the permission, should have this format:
<<action_name>>_<<class_name>>
What expression I should use to accomplish that, taking in consideration the class name is held by this.getClass().getName()?

To concatenate two strings in Spring EL you use concat function . See here for more details :
Spring EL docs
for example, I used the following :
#PreAuthorize("hasRole('ROLE_'.concat(this.class.simpleName))")

I finally solve this. I add a new method in my controller:
public String getName() {
String nome_classe = entityClass.getSimpleName();
System.out.println("getName nome_class = "+nome_classe);
return nome_classe;
}
and now I use the annotation in that way:
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")

Related

Spring Data - Using property values in projections

I have Spring Data Rest Application where I created a projection like this
#Projection(name = "UserWithAvatar", types = { User.class })
public interface UserWithAvatar {
String getName();
String getEmail();
#Value("${app.url}/pictures/#{target.imageId}")
String getAvatar();
}
The non working part is the getAvatar, it generates a url for seeing the picture.
However this ${app.url} is not working inside the projection.
How would I add this application.properties value in there?
Use #environment.getProperty('app.url') inside #{} block. It works on Spring Boot 2.3.0, i'm not sure about older versions.
Example:
#Value("#{target.pictureUrl ?: #environment.getProperty('app.url') + 'static/default-avatar.png'}")
String getAvatarUrl();

Spring REST #RequestBody consume (XML or JSON) to POJO without annotations

I am writing a Springboot REST endpoint and want to consume XML or JSON requests for a simple service. In either case I want Spring to construct an #RequestBody pojo WITHOUT annotating any of the POJO. Is this OK? Safe? Performant?
I was reading this which told me about configuration by exception. To me this means if I structure my request to contain the exact name and case as the POJO member variables I want to populate the #RequestBody will be able to create my class SomeRequest.
If this is my REST endpoint:
#RequestMapping(value = GET_FOR_SOMETHING, method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE},,
produces = {MediaType.APPLICATION_JSON_VALUE})
public #ResponseBody
StatusResponse<Boolean> getMpdForReqest(#RequestBody SomeRequest request)
And this is my POJO:
public class SomeRequest {
String one;
String two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(String two) {
this.two = two;
}
}
My JSON request:
{
"one": "str",
"two": "str"
}
My XML request:
<SomeRequest>
<one>str</one>
<two>str</two>
</SomeRequest>
My question is: why should I not do this or is it perfectly fine?
Thank you all.
TLDR; It is perfectly fine.
Is this OK? Safe? Performant?
Yes, it is as performant as it's annotated cousin, if you take program efficiency into account.
If you take the Programmer efficiency into account, it is much more efficient as the developer doesn't have to deal with a bunch of annotations.
Speaking of Programmer efficiency, I would encourage you to use project Lombok instead of crapping your POJO with bunch of getter and setter methods, that's what cool kids do now a days.
Catch
This will work fine as long as your json fields are one word and small case.
When you have multi-word field name, Java standard is the camelCase and usually JSON standard is the snake_case. In this case, you can either have a Class level Annotation (one per class, so not much ugly). Or, since you are using spring boot, you can use an application wide property (spring.jackson.property-naming-strategy = SNAKE_CASE ).
If you have weird json field names with spaces in between, you might need to use #JsonProperty annotation. Remember, this is a perfectly valid json
{
"just a name with a space" : "123"
}
POJO as RequestBody works perfectly fine. Just note that Spring however will return 400 - Bad Request for every request that can not be mapped to the #RequestBody annoted object.

Jersey 1.9 - How to get path param name and corresponding value

I need your help figuring out how to intercept a Jersey 1.9 RS call and modify a query param on a match with path param.
Let's say I have a RS class:
#Path("/bookstore")
public class BookStore {
#GET
#Path("/books/{bookName}")
public Book addBook(#PathParam("bookName") String bookName, #QueryParam("isLegacy") String isLegacy) {
return book;
}
}
I want to intercept this RS call and get the bookName from the path or query parameters. If the bookName is say "Spiderman" then I want to modify the request's query param isLegacy to false.
The example is obviously all made up.
Is their a way for me to do this via Filter, AOP or any similar mechanism? I am using Guice, Jersey 1.9.
Thank you in advance!!!
You need to create a class that extends PerRequestTypeInjectableProvider and annotate it with #Provider. By overriding getInjectable method, you can tell Jersey to modify query parameters as you described above.

Feign Client with Spring Boot: RequestParam.value() was empty on parameter 0

I created a simple Feign Client with Spring Boot like this:
#FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
#RequestMapping("/greeting")
String greeting(#RequestParam String name);
}
But when I try just to start an application I get an error:
java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
First I didn't understand what is the reason and googled a lot but didn't find an answer. Almost excidentely I figured out that it works if to write request param name explicitly:
#RequestParam("name") String name
So my question: is it a bug or could it be configured to not write request params names explicitly?
Both Spring MVC and Spring cloud feign are using the same ParameterNameDiscoverer - named DefaultParameterNameDiscoverer to find parameter name. It tries to find the parameter names with the following step.
First, it uses StandardReflectionParameterNameDiscoverer. It tries to find the variable name with reflection. It is only possible when your classes are compiled with -parameters.
Second, if it fails, it uses LocalVariableTableParameterNameDiscoverer. It tries to find the variable name from the debugging info in the class file with ASM libraries.
The difference between Spring MVC and Feign occurs here. Feign uses above annotations (like #RequestParam) on methods of Java interfaces. But, we use these on methods of Java classes when using Spring MVC. Unfortunately, javac compiler omits the debug information of parameter name from class file for java interfaces. That's why feign fails to find parameter name without -parameter.
Namely, if you compile your code with -parameters, both Spring MVC and Feign will succeed to acquire parameter names. But if you compile without -parameters, only Spring MVC will succeed.
As a result, it's not a bug. it's a limitation of Feign at this moment as I think.
Just use String greeting(#RequestParam("name") String name);
#FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
#RequestMapping("/greeting")
String greeting(#RequestParam("name") String name);
}
I use upgrade maven-compiler-plugin to solve this plobrem. you can access: https://blog.51cto.com/thinklili/2566864
This worked for me.
#FeignClient(name="session-service", url="${session.host}")
public interface SrocessingProxy {
#RequestMapping(value = "/process/{key}", method = RequestMethod.POST)
public Response processSession(#RequestParam String key, #RequestBody PayloadHolder payload);
}
//Service
#RequestMapping(value = "/process/{key}", method = RequestMethod.POST)
public Response processSession(#RequestParam String key, #RequestBody PayloadHolder payload) {
System.out.print("Key : " + key);
}

Spring Framework validate request parameter or path variable

I know I can validate forms in Spring, but can I apply similar validate to URL parameters? For example, I have a method in my controller as follows:
public String edit(#PathVariable("system") String system,
#RequestParam(value="group") String group,
ModelMap model) throws DAOException {
Can I validate the values of system and group before the method is called, to ensure they are of a certain value or match a certain regex?
Thanks
You may be able to use Spring Asserts for this. The Assert api (http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/util/Assert.html) runs a supplied expression against the specified parameters and if the expression equates to false then it throws an exception.
Ex:
Assert.isTrue(system.equals("ValidSystemName"), "You must supply a valid system");
It also contains functions to check that parameters are not null or are not empty strings, etc.
Create an annotation that marks parameters that should be validated. This annotation needs a #Retention of RUNTIME and a #Target of ElementType.PARAMETER.
Create a validator implemented as an AspectJ Aspect.
Wrap calls to controllers with this validator.
A sample annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
#Documented
public #interface ValidSystemParameter {
}
A sample validator:
#Aspect
public class ValidSystemParameterValidator {
#Pointcut("TODO: write your pointcut expression")
public void controllerMethodWithValidSystemParameter();
#Before(pointcut = "controllerMethodWithValidSystemParameter()")
public void validateSystemParameter(String systemParameter) {
// validate the parameter (throwing an exception)
}
}
To learn about the AspectJ pointcut expression language see: http://www.eclipse.org/aspectj/doc/released/progguide/language-joinPoints.html
To learn about AspectJ integration in Spring see: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj
I might be a little late, but with Spring 3.0 you have the option of using JSR-303 validation with the #Valid annotation. There are also some more specific annotations as #DateTimeFormat and #NumberFormat. More details here: http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/validation.html#validation-mvc
As I see it you have two options:
Define your request parameters as objects and user JSR-303
validation.
Use the Assert api as mentioned above.
If you just want to make a simple validation on a single value, I would go with the latter (that's what I did when I had simple int values to check for max value).

Resources