Spring Boot Health Endpoint - spring

I‘m looking for a way to change the header (add some things to it) of the health check of spring boot. I searched for this topic but I only found questions about custom health functions and so on (e.g. How to add a custom health check in spring boot health?).
That‘s not what I am looking for.
I want to use the standard/default health function (so I don‘t want to change the body of the response („status“:“UP“) nor do I want to implement my own health functionality or customize the default one.
My goal is to change the header of the response in order to achieve two things:
add some things to the header (e.g. max age)
reaching cors (e.g. I want to set allow-origin to *, allowed-method to GET and so on), I know there is a way to do it in the management properties but this applied for all endpoints and I only want these tgings to apply for the health endpoint
Is there any way to use the default health check and just modify the header and the properties like allowed origin or do I have to create a new controller?
Thanks for your help.

Just implement your own service with the methods getting the adequate metrics, and then of course your custom controller refering to it. I don't think you can modify those default endpoints..
Here's an example of this custom service:
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.MetricsEndpoint;
import org.springframework.stereotype.Component;
#Component
public class SpringActuator {
#Autowired
MetricsEndpoint metrics;
public Double getCPU() {
return metrics.metric("process.cpu.usage", Arrays.asList()).getMeasurements().get(0).getValue();
}
public Double getRAM() {
return metrics.metric("jvm.memory.used", Arrays.asList()).getMeasurements().get(0).getValue();
}
}

Related

Springboot allow access to endpoint if userId matches

I am following up from this question:
How to configure Spring Boot Security so that a user is only allowed to update their own profile
Imagine I had an end-point /user/edit/{id}, I want this to be accessible if the user either tries to edit themslves (eg: a user with ID 1 accessing /user/edit/1 but not being able to access user/edit/2) or, if they are an admin, to be able to edit any user.
Is there any way I can achieve this in the security configuration?
.antMatchers("/user/edit/**").hasRole("ADMIN")
Would restrict this to admin users, I want either admin or the id matching the user's id.
The only thing I can think of is inside the controller having something like
#GetMapping("/edit/{id}")
public void edit(#PathVariable("id") int id, Principal principal) {
User u = (User) userDetailsService.loadUserByUsername(principal.getName());
if(u.getId() == id || u.getRoles().contains("ADMIN")) {
//accept uer
}
}
But I was under the impression we shouldn't encode access logic in our controller?
It is possible to use Spring Security's Method Security Expressions to do this. Example copied from the docs:
#PreAuthorize("#c.name == authentication.name")
public void doSomething(#P("c") Contact contact);
Read the sections preceding, as there is some configuration needed. Also note that if an expression is used repeatedly you can define your own security annotations.
I was under the impression we shouldn't encode access logic in our
controller?
"Should" is maybe too strong a word, IMHO. Security expressions are powerful, and in theory would allow you to keep all security checks separate from the controller logic. Easier to spot when a check is wrong, or missing. Easier to compare with the Swagger annotations too, if you are using those to document your endpoints.
But it can get trickier when you have to do something like filter rows returned so that the user only sees some of the results. Spring Security can do that using #PostFilter. But sometimes it isn't optimal. For example, if you know that certain rows aren't going to be returned you may be able to run a faster query, rather than filter out rows after the fact.
My first Spring Security project had queries like that, so ever since I have tended to use controller logic instead of security annotations. But that's not a good reason to never use annotations! So by all means use security expressions when you can, but if you have trouble with them or other considerations arise, integrating security with your controller logic isn't so bad IMHO.
To control role access in your controller you can use annotations like #Secured or #PreAuthorize.
To use the #Secured, put in you security config class:
#EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}
And now you can use it in your controller:
#Secured("ROLE_ADMIN")
#PostMapping
public Account post(Account account, double amount){
// ...
}
To use the #PreAuthorize, put in you security config class:
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
// ...
}
And now you can use it in your controller:
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
#PostMapping
public Account post(Account account, double amount){
// ...
}
For more information you can check here the spring docs.

#Reference from Servlet Filter

I am writing a Servlet Filter and would like to use one of my Liferay components using #Reference:
package my.filter;
import my.Compo;
import org.osgi.service.component.annotations.Reference;
public class MyFilter implements Filter {
#Override
public void doFilter(...) {
compo.doTheThing();
}
#Reference(unbind = "-")
protected my.Compo compo;
}
I get this Java compilation error:
annotation type not applicable to this kind of declaration
What am I doing wrong?
Is it maybe impossible to achieve this?
As tipped by Miroslav, #Reference can only be used in an OSGi component, and a servlet filter is not one.
The solution in Liferay 7 is to develop a filter component.
The procedure to do so is explained at http://www.javasavvy.com/liferay-dxp-filter-tutorial/
You can make a simple filer like: https://www.e-systems.tech/blog/-/blogs/filters-in-liferay-7 and http://www.javasavvy.com/liferay-dxp-filter-tutorial/
But you can also use regular filters, as long you configure you Liferay webapp for that -> there are two consequences if you use regular filters though: you will be out of osgi application and you will have to keep track of this whenever you update your bundle. That is why you should not go with regular implementation. (just complementing the OP answer with the underlining reason to avoid the initial track)

How to map a path to multiple controllers?

I'm currently working on a spring based web application and have a special requirement that seems not (at least not out of the box) be provided by spring MVC. The application serves data for multiple users each organized in their own "company". Once a user has logged in, I'm able to identify to which company he belongs to.
The application itself is built with multiple "modules", each with it's own domain objects, DAO, Service and Controller classes. The idea behind this concept is that I can for example extend a certain controller class (let's say to use a different service class) based upon the user and here is my problem.
Since i do not want to change my request paths for certain users, I'm currently looking for a way how to serve a request issued on a certain request path with different instances of a controller based upon the user issuing the request.
I came up with the idea to attach a HTTP Header Field for the company
Example:
X-Company:12345
and have my controllers configured like this:
#Controller
#RequestMapping(value="/foo/")
public class FooController {
// ...
}
#Controller
#RequestMapping(value="/foo" headers="X-Company=12345")
public class SpecialFooController extends FooController {
// ...
}
However this is not possible, since spring MVC treats each header (except Content-Type and Accept) as a kind of restriction, so in my case it would handle all requests with the FooController instead of the SpecialFooController unless i add a "headers" restriction on the FooController as well, which is not practicable.
Is there some way how to customize this behaviour or some direction one could point me to look for? Or maybe someone has another idea how to achieve this. It'll be highly appreciated.
Thanks!
I'am not sure but I think you can do this with HandlerMapping. Have a look at the documentation
To take your own suggestion, you can use the #RequestHeader annotation in your controller methods:
#Controller
public class MyController {
#RequestMapping("/someAction")
public void myControllerMethod(#RequestHeader('X-Company-Id') String companyId) {
}
}
Or you could use #PathVariable:
#Controller
public class MyController {
#RequestMapping("/{companyId}/someAction")
public void myControllerMethod(#PathVariable("companyId") String companyId) {
}
}
Using this approach would mean that it is in fact different URLs for each company, but if you can set the company id header, I guess you also can suffix the URLs with the company id.
But there are also other possibilities. You could write an interceptor that puts the company id in a session or request variable. Then you wouldn't have to add the annotation to every controller method. You could also use a subdomain for each company, but that wouldn't look too pretty if the company id is a random alphanumeric string. E.g: companyone.mydomain.com, companytwo.mydomain.com
Edit
#RequestMapping can be added to the controller level as you know, so you should be able to do
#Controller
#RequestMapping("/controller/{companyId}")
as the base url, if that's a better option.
I was able to meet the requirement by making usage of a customized RequestCondition. By defining your own annotation that can be placed at the type and method level of a controller. Extending the RequestMappingHandlerMapping by your own implementation and overriding the getCustomTypeCondition() and getCustomMethodCondition() methods translates a controller annotation into your own RequestCondition.
When a request comes in, the custom RequestCondition will be evaluated and the annotated controller(method) will then be called to serve the request. However this has the downside, that one needs to remove a servlet-context.xml file and switch to the WebMvcConfigurationSupport class instead in order to be able to use your customized RequestMappingHandlerMapping class.
This question was also discussed here.
Edit:
A pretty good example using this can be found here.

How to client-side validation and server-side validation in sync?

Typically when writing a web-app we want to perform validation on both client side to offer immediate feedback and on server-side to ensure data integrity and security. However, client-side browser apps are typically written in JavaScript. Server-side can be written in Java, Php, Ruby, Python and a host of other languages. When server-side is backed by something like node.js, it is really easy to re-use the same validation code on both client and server, but if server-side is based on Rails or Django (or whatever other framework you can name), what's the best way to make sure the validation code are kept on sync? It seems a bit redundant to have to re-implement the same code in multiple languages.
If you keep the following persepective in mind, it may seem okay to duplicate certain validations.
Let's break validations into two parts. A) Business Validations e.g. "Amount in Field X should be greater than $500 if if checkbox Y is checked" B) Basic data validations e.g. datatype checks, null checks etc. (We may debate that every validation is business validation but that is purely context specific).
Category A: It is part of your business logic and should be kept only on server side.
Category B: Validations of this type are potential candidates to be placed on the client side. But keep in mind that browser side validation can be bypassed. This does not imply that you should not have validations on browser side at all but such validations should be considered merely a bonus to save network roundtrip from server. Server must re-perform these validations.
In nutshell, validations should not be considered as unit of reusable code across tiers. Their objective varies and should allow redundancy.
Hope this helps.
From projects I've seen there's three general strategies:
Fully duplicate client-side and server-side validation. This would require two different codebases in the case of javascript frontend and java/c#/ruby backend. You'd have to manually keep the logic of both in sync.
Do minimal client-side validation. Check for only very basic stuff. Have server side do the full validation. Have server side pass a validation-errors object of some kind back to the client and have client logic to translate that into UI messages (error messages, red borders, etc). Asp.net MVC framework is roughly an example of this model.
Use ajax to make validation calls into your server side when the user changes or leaves each control. This can allow all your validation on the server side, and will reduce the feedback wait time for the user, but can increase client to server side traffic immensely.
In my experience, option 1 is generally less of a pain point than maintaining the extra code and complexity required for option 2 and 3.
Our architecture allows for validator code-sharing: Our back-end is JAVA. Front-End is JavaScript/TypeScript (Angular if that matters). The JAVA back-end did all business stuff. For reporting MySQL to user and some utility user preferences we also had a minimal back-end written in NodeJS/Express.
To re-use the validation, we changed some endpoints from JAVA to NodeJS, then the NodeJS did the validation and internally passed the request body to the JAVA. (by system-call or non-public API). Then we removed the validation from JAVA code. NodeJS/Express mostly acts as a proxy, it also checks for requests validity.
That way we could share TypeScript codes/classes between Front-End and our minimal Back-End.
The overheads added:
need to run a NodeJS back-end separately from main back-end (one extra process to manage/watch)
need to expose a way to call your methods/APIs internally in the system (and make sure they are not public as they'll no longer have validators)
some extra Nginx routing
i build this serve side validation endpoint:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.hibernate.validator.HibernateValidator;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
#RestController
#AllArgsConstructor
public class ValidationEndpoint {
static {
Locale.setDefault(Locale.GERMAN);
}
private final ObjectMapper objectMapper;
private final Validator validator;
#PostMapping("/")
public Map < String, List < String >> validate(#RequestBody String entity, #RequestParam String className) throws ClassNotFoundException, JsonProcessingException {
Class c = Class.forName(className);
Object jsonObject = objectMapper.readValue(entity, c);
return validator.validate(jsonObject).stream().map(constraintViolation -> {
Violation violation = new Violation();
violation.setMessage(constraintViolation.getMessage());
violation.setFieldName(constraintViolation.getPropertyPath().toString());
return violation;
}).collect(groupingBy(Violation::getFieldName, Collectors.mapping(Violation::getMessage, Collectors.toList())));
}
#PostMapping("/valida")
public List < String > validateField(#RequestBody String entity, #RequestParam String className, #RequestParam String propertyName) throws ClassNotFoundException, JsonProcessingException {
Class c = Class.forName(className);
Object jsonObject = objectMapper.readValue(entity, c);
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(false)
.defaultLocale(Locale.GERMAN)
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator.validateProperty(jsonObject, propertyName).stream().map(constraintViolation -> {
Violation violation = new Violation();
violation.setMessage(constraintViolation.getMessage());
violation.setFieldName(constraintViolation.getPropertyPath().toString());
return violation;
}).map(Violation::getMessage).toList();
}
}

Spring boot actuator - Implement custom metrics

I would like to implement custom metric or statistics to my spring boot rest web service using actuator but i am not able to find simple tutorials.
For example:
how to show how many times a certain controller was called and what exact parameter field was filled?
how can i create a metric that when its URL is called, it runs certain query and shows back a json with some result
This seems like a good scenario for AOP (Aspect Oriented Programing) as this will allow you to separate this statistic logic from the business logic.
Have a look at Spring doc for more info about AOP and how to achieve that with Spring.
You can then define a pointcut on your controller and have a service for counting (and probably then storing) the data.
Refer below link
AOP Example
For point two the solution is to create an endpoint class (it can be or not a rest controller class). For example:
#Component
#RestControllerEndpoint(id = "pfm-statistics")
public class StatisticsEndpoint {
#GetMapping(value = "/", produces = "application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet")
#ResponseBody
public byte[] generateStatisticsAsExcel() {
...
Note that the ID is the path to be called from URL. We can create a simple endpoint too and just return a string if we want. In this case instead of #RestControllerEndpoint annotation we can use #Endpoint, as a side note, the id should always contain dash

Resources