Spring boot + Caffeine cache + Check header - java

i'm trying to use cache (caffeine) with Spring boot and im having a problem. I need to check the header "header-name" in every call but application is caching it so after first request with the right header, dont matter what header i send and the application will not check it and is just returning data from the cache, is there anyway that i can force spring to check header and then get data from cache?
#GetMapping("/request-a")
#Cacheable(cacheNames = "cachename", key = "#root.methodName")
public ResponseEntity<?> makeRequest(#RequestHeader("header-name") String headerName) {
this.authConfig.headerCheck(headerName);
/*
code
*/
}
I already used header "Cache-Control:no-cache" and didnt resolve my problem.
Thanks in advance.
Edit1: method "headerCheck" just check if its equal to another String or not null.

Found a solution:
Create a classe that implements HandlerInterceptor and use preHandle method.
#Component
public class CheckHeaderInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// validate what you want, on error return false
// if everything its ok, return true
}
}
Then register the handler with:
public class WebMvcConfig implements WebMvcConfigurer {
#Autowired
private CheckHeaderInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("url that you wannna use handler");
}
}

Related

Is there a way to get request URI in Spring?

Whenever a request is made, I need to get the request URI for some internal calculations.
For some time I've been doing it like this:
public Mono<Response> example(ServerHttpRequest req) { ... }
And then using req.getURI(), but that becomes a pain once you need to pass it down multiple times. I need the URI object to extract scheme, schemeSpecificPart, host, port from it.
Is there a way to get these properties without extracting them from a request?
UPD: I see that for Web MVC there are convenient methods to retrieve request URI. But I need the same for reactive stack (netty).
It can be achieved by creating WebFilter that puts ServerHttpRequest into the Context:
#Component
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveRequestContextFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
return chain
.filter(exchange)
.contextWrite(ctx -> ctx.put(ReactiveRequestContextHolder.CONTEXT_KEY, request));
}
}
Additionally, create a class that provides static access to request data:
public class ReactiveRequestContextHolder {
public static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;
public static Mono<ServerHttpRequest> getRequest() {
return Mono.deferContextual(Mono::just).map(ctx -> ctx.get(CONTEXT_KEY));
}
public static Mono<URI> getURI() {
return getRequest().map(HttpRequest::getURI);
}
}
Methods can be accessed through the class name directly without having to instantiate them. Just be aware that it should not be accessed before the filter is executed.
Example of usage:
#RestController
#RequestMapping
public class TestController {
#GetMapping("/test")
public Mono<URI> test() {
return ReactiveRequestContextHolder.getURI();
}
}
Reference
You can try this :
public Mono<Response> example(WebRequest request){
System.out.println(request.getDescription(false));
.......
}
You can turn this false to true in getDescription as false will only give you the Uri which i think is the only thing you need.
You can inject it in any bean.
#Autowired
private HttpServletRequest request;

Spring boot - Adding data read only

I am thinking what would be best solution for following case. Suppose we have at start CRUD app - using Spring Boot. I would like to add read only state for this application - which allows only data read and blocks create, update, delete data operations for admin role. I think about adding aspect (#Aspect) which checks current app state (which is saved in db) and starts if create, update, update operations are invoked. If app is in read-only state - exception will be thrown (handled by #ControllerAdvice)
I wonder if adding aspect is the best option - I dont want modify existing code. Whats your take on that? Moreover - how would you write integration tests for #aspect - testing if aspect starts properly? How could be aspects tested for this case? What are good practises for testing #aspects (integration tests #springboottest)
This honestly seems like an inconvenient way of doing this. Why not just add an Interceptor that checks for this? I did something similar before
#Component
#RequiredArgsConstructor
public class ReadOnlyModeInterceptor implements HandlerInterceptor {
private final ServerProperties serverProperties;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (serverProperties.isReadOnlyMode()) {
String method = request.getMethod();
boolean isReadOnlyMethod = "GET".equals(method) || "OPTIONS".equals(method);
String servletPath = request.getServletPath();
boolean isReadOnlyPath = isReadOnlyPath(servletPath);
if (!isReadOnlyMethod && isReadOnlyPath) {
throw new ServiceUnavailableException("Server is in read-only mode.");
}
}
return true;
}
private boolean isReadOnlyPath(String servletPath) {
if (serverProperties.isFullyReadOnly()) {
return true; // wildcard option, entire server is read-only
}
return serverProperties.getReadOnlyPaths().stream().anyMatch(servletPath::contains);
}
}
You also need to register it
#RequiredArgsConstructor
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final ReadOnlyModeInterceptor readOnlyModeInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(readOnlyModeInterceptor).order(0);
}
}

Spring Security Custom Header Request Matcher Not Working

I have a requirement where using spring security I want that if a request of any type contains a particular header with a particular value then only it should be allowed to access the api's otherwise not. Below is my configuration code:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter{
public AppSecurityConfig() {
}
#Autowired
public void configure​(HttpSecurity http) throws Exception {
http.authorizeRequests().requestMatchers(new CustomHeaderRequestMatcher()).permitAll();
}
}
Below is the customer header request matcher business logic:
public class CustomHeaderRequestMatcher implements RequestMatcher{
public CustomHeaderRequestMatcher() {
}
#Override
public boolean matches(HttpServletRequest request) {
if(Objects.nonNull(request.getHeader("my-token"))
&& request.getHeader("my-token").equals("abc")) {
System.out.println("true");
return true;
}
System.out.println("false");
return false;
}
}
But here I could see that even when I am not passing the custom header "my-token" in my request it is allowing to access the api. For every request though custom request matcher class is being called. I am confused as to why spring security is not handling the case?
you have not blocked all other requests. spring sees the first rule and doesn't know what to do next. you could add a "anyRequest().denyAll()" or "anyRequest().authenticated()" after the "permitAll()"

Spring Boot Request Header Validation

I am using Spring Boot 2.3.8 for creating rest services. I have a requirement to validate the request headers such that it should have certain headers otherwise throw error. This should be common for all the methods or services. I tried below,
public ResponseEntity<Object> addEmployee(
#RequestHeader(name = "header1", required = true) String header1,
#RequestHeader(name = "header2", required = true) String header2,
#RequestBody Employee employee)
throws Exception
{
But I need to add this for all the methods in all the controllers. If this is the case how can I throw an error like "Header1 missing in request headers" / "header2 missing in request headers" for all the services globally ?
For global use you can register an interceptor.
#Component
public class MyHandlerInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3) throws Exception {
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView model) throws Exception {
}
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//here check headers contained in request object
if (request.getHeader("header1") == null || request.getHeader("header2") == null) {
response.getWriter().write("something");
response.setStatus(someErrorCode);
return false;
}
return true;
}
And then register it
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Autowired
private MyHandlerInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("/**");
}
}
This is what filters are for. You want to filter out requests based on a header being there or not and return an error if its missing.
Extend OncePerRequestFilter optionally override shouldNotFilter if you do not want it to be used for all request OR you could implement a normal Filter and use FilterRegistrationBean to register it only for a specific path.
then in the filter you could throw an exception and let Spring figure out how to display that, or actually set the response to something meaningful.
If you have Zuul in place then you can read the request header attributes there in pre route and on validation failure reply back to the request with error populated.
This is a cross-cutting concern and should be done using AOP. Since you're using Spring, you can do the following:
Create an annotation called ValidateHeaders:
public #interface ValidateHeaders {}
Create a #Before advice that intercepts methods annotated with #ValidateHeaders:
#Before("#annotation(com.somepackage.ValidateHeaders)")
public void controllerProxy(JoinPoint jp) {
Object[] args = jp.getArgs();
//validation logic, throw exception if validation fails
}
Note that you'll have to extract the fields using reflection as:
Annotation[][] pa = ms.getMethod().getParameterAnnotations();
You can iterate through all the annotations and search for request headers as:
if(annotations[i][j].annotationType().getName().contains("RequestHeader")) {
RequestHeader requestHeader = (RequestHeader) m.getParameterAnnotations()[i][j];
//now access fields in requestHeader to do the validation
}
Here's an article that can get you started with Advice types:
https://www.baeldung.com/spring-aop-advice-tutorial

Custom interceptor returns but request doesn't hit function in controller

I am developing spring boot project. I have a HandlerInterceptorAdapter, in where I have some logic to intercept and check request data in preHandle(...).
public class MyCustomInterceptor implements HandlerInterceptorAdapter{
// I only override the preHandle(...)
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response){
//my custom logic here.
// if a condition is not met, I return false, my understanding is that it would stop this request from proceeding forward
if(conditionNotMet) {
log.info("Condition NOT met, returns false!")
response.setStatus(400);
return false;
}
log.info("Condition met, returns true!")
// otherwise, returns true
return true;
}
}
In above interceptor's preHandle(), I have some custom logic, then I check if certain condition is not met, I return false to stop proceeding forward the request, otherwise I return true there. My understanding is that if I return true eventually, the request would hit the corresponding rest controller.
The interceptor is registered and functional well:
#Configuration
public class MyConfigurationAdapter implements WebMvcConfigurer {
#Autowired
private MyCustomInterceptor myCustomInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myCustomInterceptor);
}
}
I have a rest controller:
#RestController
#RequestMapping(value = "/api/myservice/")
public class MyController {
#PostMapping(value = "add_note")
public MyResponse addNote(#Valid #RequestBody AddNotePayload payload) {
// I see logs in interceptor, it returns true but I don't see this log
log.info(" Start adding note...");
...
}
The URL maps to the addNote(...) function of above controller is POST http//localhost:8080/api/myservice/add_note. When I fire to this endpoint on Postman, I see the logs in preHandle() of my interceptor that the condition there is met and preHandler() returns true. Because it returns true there, so I expected that the request would be proceeded forward to controller level, but I don't see the log in addNote() of MyController. Why? What could be the reason?

Categories

Resources