I would like my HandlerInterceptor, in my spring boot application, not to run on requests coming in on the management port.
management.port = 9091
I can think of 2 ways to do it, but i'm looking for a more standard way.
One will be to check the port from within the HandlerInterceptor:
#Override
public boolean preHandle(#NonNull HttpServletRequest request, #NonNull HttpServletResponse response, #NonNull Object handler) {
if(request.getLocalPort() == managementPort){
return true;
}
else{
....
}
}
The second will be to exclude all paths, related to the managementPort when registering the interceptor:
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor).excludePathPatterns(managementPortEndpoints);
}
This can easily get out of sync.
Is there a more native way to achieve it? (spring configuration of some sort)
I choose javax.servlet.Filter for filtering requests.
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class BlockFilter implements Filter {
private int managementPort = 9091;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request.getRemotePort() == managementPort) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).setStatus(401);
}
}
}
Once all accesses to management.port are requests made for URLs that start with /actuator - spring will not run the filters and interceptors on the requests.
So, that seems like the way to go about it
Related
i want to create a filter/bean that have autowired constructor parameters and for every request to some urls checks the header and returns a pojo error (or ideally throws the exception that will be handled by spring), or passes the request through the filter chain. something like
#Bean
class MyFilter {
#Autowired
public MyFilter(MyService myService) {...}
public doFilter(request, response, chain) {
if (request. ...) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST)
// or return new MyErrorPojo()
}
return chain.doFilter(request, response)
}
}
what's the spring's way of doing this?
Maybe this will help you.
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
#Component
#Order( 1 )
public class RequestFilter implements Filter
{
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain ) throws IOException, ServletException
{
HttpServletRequest req = ( HttpServletRequest ) request;
if( false )
{
response.getWriter().println( "error" );
throw new ServletException( "" );
}
else
{
doFilter( request, response, chain );
}
}
}
I think here you have a rather confusing mix of technologies:
Filters belong to servlet specification and operate at the HTTP request level of abstraction.
Once the filter chain passes and comes to DispatcherServlet (an entry point to spring mvc) spring comes into play with all the MVC goodies.
So if you want to check the header there is an abstraction called org.springframework.web.servlet.handler.HandlerInterceptorAdapter
You can basically create a class that implements this interface, and check the header in the method: that belongs to this interface
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
As you see, you have an access to the request and its possible to check the headers there.
Now I haven't used it by myself (can't check right now) but it requires the configuration at the level of WebMvcConfigurer that has a special "addInterceptor" method. So I can't confirm that just adding a bean of the created type is enough or you whould configure it with WebMvcConfigurer
I have two micro services(Spring Boot) for integration between two Product.My architecture is like below.
Product1 <-------> microservice1/microservice2 <------> Product1.
My task is I need to track requests with input prams (with methods, eg. GET, POST, etc), request path, query string, corresponding class method of this request, also response of this action, both success and errors and save into database table.
I have tried spring boot actuator. But no luck.
Please suggest how to achieve that.
I suggest using a HandlerInterceptor that you can register to the two micro-services REST controllers. It has a nice lifecycle call-backs that you can use to serialize the data and save it into the database. Here's the custom interceptor class:
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#Component
public class DBRequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// DB logic
return super.preHandle(request, response, handler);
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// DB logic
super.afterCompletion(request, response, handler, ex);
}
}
Once you had developed this re-usable handler, you can register in the web configurer. Here's the code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class WebConfiguration implements WebMvcConfigurer {
#Autowired
private DBRequestInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("/**/");
}
}
This code registers the interceptor on all of your URL mappings. You can customize it to some specific services URLs.
You might find an exception in the original code once you read the request stream in the above interceptor. In that case, you can leverage the Spring wrapper classes ContentCachingRequestWrapper
Hope that helps.
I had developed rest API on spring boot application. The APIs accept only GET , and POST , but on requesting using OPTIONS method , API responding 200 status (instead of 405). I googled this issue , but none of the solution was springboot based .
Response:
Allow: OPTIONS, TRACE, GET, HEAD, POST
Public: OPTIONS, TRACE, GET, HEAD, POST
Need to disable OPTIONS method.
Previous answer is for tomcat only, so adding mine as well. You can disable the method cross-container by, for example, using a standard servlet filter:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
#Component
public class MethodFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (request.getMethod().equals("OPTIONS")) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
} else {
filterChain.doFilter(request, response);
}
}
}
Note: it is assumed that this class is componentscanned by Spring. If not, you can use other registration methods as detailed in here.
Try this; in allowedMethods you can filter methods which are required:
#Configuration
public class CorsConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(origins u want to allow)
.allowCredentials(false).allowedMethods("POST", "GET", "PUT");
}
};
}
}
I tried this and it worked.
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container.getClass().isAssignableFrom(TomcatEmbeddedServletContainerFactory.class)) {
TomcatEmbeddedServletContainerFactory tomcatContainer = (TomcatEmbeddedServletContainerFactory) container;
tomcatContainer.addContextCustomizers(new ContextSecurityCustomizer());
}
}
};
}
private static class ContextSecurityCustomizer implements TomcatContextCustomizer {
#Override
public void customize(Context context) {
SecurityConstraint constraint = new SecurityConstraint();
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.setName("restricted_methods");
securityCollection.addPattern("/*");
securityCollection.addMethod(HttpMethod.OPTIONS.toString());
constraint.addCollection(securityCollection);
constraint.setAuthConstraint(true);
context.addConstraint(constraint);
}
}
If you are using spring security, you can use the method below:
#Bean
public HttpFirewall configureFirewall() {
StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();
strictHttpFirewall.setAllowedHttpMethods(Arrays.asList("GET","POST","OPTIONS"));
return strictHttpFirewall;
}
We have unmanaged extension. We implemented custom communication API between server and client.
Now we need to ensure that client and server have same API version.
One solution - verify version in each resource. But this approach is messy and leads to code duplication.
What we want is to implement our own Filter and add it to Neo server.
Is this possible? If yes - then how?
This is possible!
Approach is a bit tricky and fragile, but it's working (blog post).
Dependency
You need neo4j-server dependency, because it contains SPIPluginLifecycle that is needed to get access to Neo4j web server.
So, add to your pom.xml:
<dependency>
<groupId>org.neo4j.app</groupId>
<artifactId>neo4j-server</artifactId>
<version>${version.neo4j}</version>
</dependency>
Filter
Create your filter. Let's take this one for example:
public class CustomFilter implements Filter {
public CustomFilter() {
}
#Override
public void init(final FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(final ServletRequest request,
final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
#Override
public void destroy() {}
}
This filter doesn't do anything usefull - just continue chain further.
Lifecycle plugin
Now tricky part. We need to:
Implement SPIPluginLifecycle
Get web server
Add filter to web server
Code:
public final class ExtensionPluginLifecycle implements SPIPluginLifecycle {
private WebServer webServer;
private CustomFilter customFilter;
#Override
public Collection<Injectable<?>> start(final NeoServer neoServer) {
webServer = getWebServer(neoServer);
addFilters();
}
#Override
public void stop() {
removeFilters();
}
#Override
public Collection<Injectable<?>> start(final GraphDatabaseService graphDatabaseService,
final Configuration config) {
throw new IllegalAccessError();
}
private WebServer getWebServer(final NeoServer neoServer) {
if (neoServer instanceof AbstractNeoServer) {
return ((AbstractNeoServer) neoServer).getWebServer();
}
throw new IllegalArgumentException(String.format("Expected: [AbstractNeoServer], Received: [%s].", neoServer));
}
private void addFilters() {
customFilter = new CustomFilter();
webServer.addFilter(customFilter, "/extension-path/*");
}
private void removeFilters() {
webServer.removeFilter(customFilter, "/extension-path/*");
}
}
Tricky part is not so "legal" access to web server. This can break in future, so be carefull.
Note addFilters() and removeFilters() methods - this is why we have been done all this way.
Important: lifecycle plugin should be registered as service:
// file: META-INF/services/org.neo4j.server.plugins.PluginLifecycle
my.company.extension.ExtensionPluginLifecycle
I've got a project which is divided in different modules, you have for example a site and a forum.
The forum can be found at:
http://example.com/[forum]/
and for example can be:
http://example.com/support/
http://example.com/helpme/
http://example.com/aforum/
The site can be found at:
http://example.com/[site]/
and for example can be:
http://example.com/site1/
http://example.com/nice/
http://example.com/something/
The [forum] and [site] part are variable. In my database i lookup that "nice" is a site, "helpme" is a forum.
I have a spring RequestMapping for my ForumController:
#RequestMapping(value = { "/{simpleTitle:[0-9a-z-]+}" }, method = RequestMethod.GET, produces = "text/html")
public void list(#PathVariable String simpleTitle, Model model, HttpServletRequest req, HttpServletResponse resp) {
I have the samething for sites, so a SiteController:
#RequestMapping(value = { "/{simpleTitle:[0-9a-z-]+}" }, method = RequestMethod.GET, produces = "text/html")
public void list(#PathVariable String simpleTitle, Model model, HttpServletRequest req, HttpServletResponse resp) {
This of course goes bad, cause 2 controllers with the same requestmapping isn't good.
I can create a FrontController which with above request mapping, lookup what simpleTitle is (a forum or a site) and call functions to display a forum or a site. That works.
But it isn't very "spring" like and structured.
Is it possible "intercept" a request and internally forward (or call the function) on the controller myself?
This way i could have Interceptor which looks a simpleTitle, decides whether it is a forum or site, and "forwards"/"calls" the right controller.
Frankly I like #Luiggi Mendoza solution, but if you want an alternative, use something like this:
package eu.europa.acer.aris.ceremp.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
#Component(value = "yourCustomFilter")
public class YourCustomFilter extends OncePerRequestFilter{
private static final Logger logger = LoggerFactory.getLogger(YourCustomFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//String[] path = request.getRequestURL().toString().split("/");
if (letPagePass(request.getRequestURL().toString()) == false)
{
// if request is bound to static resource like js//img do nothing, the filter chain will activate
if (letResourcePass(request.getRequestURL().toString()))
{
}
else
{
String[] urlInfos = obtainUrlAndParametersLast(request.getRequestURL().toString());
// last element will always give back last part including any parameter
// first element will always be a controller modifier
response.sendRedirect(request.getContextPath()+rebuildControllerPath(urlInfos));
return;
}
}
filterChain.doFilter(request, response);
}
private String rebuildControllerPath(String[] pathElement )
{
//do your thing here
if ("forum".equals(pathElement[0]))
{
String addenda = "/forumController/";
for (String singlePart: pathElement)
{
addenda = addenda+singlePart+"/";
}
return addenda;
}
}
// bind forceful redirect
public boolean letPagePass(String url)
{
// if you have some page that are naturally unique among controllers that you want always to process natively
String[] path = url.split("/");
if (path[path.length-2].startsWith("yourCertainUrlIgnoringParameters"))
{
return true;
}
// directcall
else if (path[path.length-2].startsWith("yourCertainUrlIgnoringParameters2"))
{
return true;
}
else
{
return false;
}
}
public boolean letResourcePass(String url)
{
String[] path = url.split("/");
/*
this assumes you have always a strict structure so your image//js//reource will always be in
https://domainname/a/lot/of/folder/img/actuaresource.png
or
https://domainname/a/lot/of/folder/js/actuaresource.js
etc
*/
//image pass
if (path[path.length-2].equals("img") || url.contains("/img/"))
{
return true;
}
//css pass
else if (path[path.length-2].equals("css") || url.contains("/css/"))
{
return true;
}
//js pass
else if (path[path.length-2].equals("js") || url.contains("/js/"))
{
return true;
}
else
{
return false;
}
}
#Override
public void destroy() {
}
}
and add to your web.xml file the following xml snippet
<!-- your fi Filter -->
<filter>
<filter-name>yourCustomFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>yourCustomFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Cheers Luiggi Mendoza and witchedwiz for the great ideas!
While i was eating i came up with a very simple solution: use servlet filter. Add a request header with a HttpServletRequestWrapper (type: forum or type: site) inside the doFilter.
In the #RequestMapping add a headers property like headers = "type=forum".
Now #RequestMapping can have the same urls, they differ in request header. And all spring functionality works, although i only made a simple test case so far.
WebApplicationContextUtils helped to get spring beans in the servlet filter.