I'm starting a Grizzly server as follows:
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build();
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.packages("com.example");
resourceConfig.property("contextConfig", applicationContext);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig, false);
server.start();
In the package com.example, I have filters as follows:
#Component
#Provider
#Priority(0)
public class MyFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest httpServletRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
/* httpServletRequest is null here */
}
}
My filter is instantiated as expected by Spring. Also, JAX-RS detected it and uses the same instance instantiated by Spring. I'm trying to access the underlying HttpServletRequest but I can't find how.
I'm aware that the servlet request will never be injected until the filter instance is created as a proxy since the servlet request is request scoped. I tried to annotate the filter with #RequestScope, same thing.
You need to use the GrizzlyWebContainerFactory from
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>${jersey2.version}</version>
</dependency>
if you want to create servlet container. Currently, you are not creating a servlet container, that's why there is no HttpServletRequest available.
You can do something like
GrizzlyWebContainerFactory.create(baseUri, new ServletContainer(resourceConfig));
Related
I want to be able to handle 401 and show a specific page in angular 8 but currently only showing index.html file
Things to mind
Angular is the view for the spring boot so its not a separate application
I am not using spring security. Im just using filters in spring to determine if to be authorize
This is my filter.
#Component
public class CustomSessionFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) httpServletRequest;
if (!req.getRequestURI().startsWith("/sample/path")){
HttpServletResponse httpResponse = (HttpServletResponse) httpServletResponse;
httpResponse.setContentType("application/json");
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
}
Maybe its relevant that i have a Controller that extend ErrorController
#CrossOrigin
#RestController
public class IndexController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public ModelAndView saveLeadQuery() {
return new ModelAndView("forward:/");
}
#Override
public String getErrorPath() {
return PATH;
}
}
EDIT: I didnt use spring security because i dont need to login i just have to go through specific path and do some authentication.. and there is no user for the application
I just wanted to place my 0.02$ here.
In order to secure a route in your application, you can create a Security Config that extends WebSecurityConfigurerAdapter, where you can protect certain routes through either antMatchers or mvcMathchers (second is recommended). Furthermore, there you can declare a set of roles or conditions (via Spring Expression Language) that will automatically throw a 401 Error in case a user that does not have an access is trying to access the route.
More about that you can find here https://www.baeldung.com/security-spring
As of ErrorController, I believe that controller advice would be more suitable for this use case. It pretty much intercepts all errors that you declare and can return more informative and generic response :)
More about that https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
As
Dependencies from my pom:
2.2.5.RELEASE for Spring
3.3.5 for CXF
spring-boot-starter
spring-boot-starter-actuator
spring-boot-starter-web
spring-boot-devtools
spring-boot-configuration-processor
spring-boot-starter-tomcat
spring-boot-starter-test
cxf-spring-boot-starter-jaxws
cxf-rt-features-logging
Here are the server settings defined in the application.yml:
server:
port: 8080
servlet:
context-path: /cs
The first Servlet is a CXF JAXWS Endpoint configured like so:
// https://github.com/apache/cxf
#Bean(name=Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public IFileNetWSSoap documentService() {
return new DocumentServiceEndpoint();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), documentService());
endpoint.setServiceName(fileNetWS().getServiceName());
endpoint.setWsdlLocation(fileNetWS().getWSDLDocumentLocation().toString());
endpoint.publish(properties.getDocumentEndpoint());
Binding binding = endpoint.getBinding();
((SOAPBinding)binding).setMTOMEnabled(true);
return endpoint;
}
Currently listening at this address: http://localhost:8080/cs/services/document-service_1.0
The second Servlet is javax.servlet.http.HttpServlet (TomCat right now):
#WebServlet(urlPatterns = {"/image-service_1.0"})
public class ImageServiceEndpoint extends HttpServlet {
#Autowired
private BusinessService businessServices;
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
doGet(request, response);
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
this.businessServices.imageRetrieval(request, response);
}
}
Currently listening at this address: http://localhost:8080/cs/image-service_1.0
And finally, there is the Spring-Boot Actuator Servlet.
Currently listening at this address: http://localhost:8080/cs/actuator
My question is "How can I configure the WebServlet to listen on the CXF segment without breaking everything?" e.g. http://localhost:8080/cs/services/image-service_1.0
It has been brought to my attention that maybe I should use a Spring MVC Controller in lieu of the Servlet. The only requirement I have for this endpoint id to take in query string parameters and stream binary content back to the caller.
You can't take over the same path as CXF WS Endpoint (default: /services)
I have Spring Boot application with REST API mapped on /api. I need to define additional servlet on /. I want all request that match /api was handled by REST API and all others requests by the servlet. How to do this?
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#RestController
#RequestMapping("/api")
public class ApiController {
#GetMapping
public String get() {
return "api";
}
}
#Bean
public ServletRegistrationBean customServletBean() {
return new ServletRegistrationBean<>(new HttpServlet() {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().println("custom");
}
}, "/*");
}
}
In code above I want something like this:
curl http://localhost:8080/api/
> api⏎
curl http://localhost:8080/custom/
> custom
I have tried with filter to redirect requests, but all requests go to custom servlet:
#Bean
public FilterRegistrationBean apiResolverFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter((req, response, chain) -> {
HttpServletRequest request = (HttpServletRequest) req;
String path = request.getRequestURI().substring(request.getContextPath().length());
if (path.startsWith("/api/")) {
request.getRequestDispatcher(path).forward(request, response);
} else {
chain.doFilter(request, response);
}
});
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
This project is available on github: https://github.com/mariuszs/nestedweb
When mapping a servlet to the root path you will override the mapping for the DispatcherServlet which, by default, is mapped to /.
There are basically 3 solutions you could try
Map the DispatcherServlet to /api and modify the mappings in your controllers
Use a ServletForwardingController to forward the request to the configured but unmapped Servlet
Use a ServletWrappingController to wrap a Servlet instance
Number 2 and 3 are almost the same, with this difference that with option 3 Spring also manages the Servlet instance whereas with option 2, the Servlet container manages the Servlet.
Mapping DispatcherServlet to /api
Option 1 can be an option if all of your controllers are mapped under /api, if they aren't this isn't an option. In your application.properties you would set the spring.mvc.servlet.path to /api. Then you would configure your other Servlet like you did in your question.
Use a ServletForwardingController
Spring provides a ServletForwardingController which will lookup a Servlet in the ServletContext given the name of the servlet and forward the request to it. You will still have to register the Servlet but prevent it from being mapped.
Next you would need a SimpleUrlHandlerMapping to map the URLs to this controller or set it as the default handler (basically a catch all).
#Bean
public ServletForwardingController forwarder() {
ServletForwardingController controller = new ServletForwardingController();
controller.setServletName("my-servlet");
return controller;
}
#Bean
public CustomServlet customServlet() {
return new CustomServlet();
}
#Bean
public ServletRegistrationBean customServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(customServlet(), false);
registration.setServletName("customServlet");
return registration;
}
#Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setDefaultHandler(forwarder());
mapping.setOrder(LOWEST_PRECEDENCE - 2);
return mapping;
}
Use a ServletWrappingController
Spring provides a ServletWrappingController which will internally create and configure a Servlet instance. It acts as an adapter from/to the Servlet to a Spring Controller. You don't have to register the CustomServlet in this case and is thus slightly easier to configure the then ServletForwardingController.
Next you would need a SimpleUrlHandlerMapping to map the URLs to this controller or set it as the default handler (basically a catch all).
#Bean
public ServletWrappingController wrapper() {
ServletWrappingController controller = new ServletWrappingController ();
controller.setServletName("my-servlet");
controller.setServletClass(CustomerServlet.class);
return controller;
}
#Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setDefaultHandler(wrapper());
mapping.setOrder(LOWEST_PRECEDENCE - 2);
return mapping;
}
Depending on your architecture and url structure you might want to go for option 1 or option 3.
I have a function with #GetMapping(value = "/getToken") that writes json content.
#GetMapping(value = "/getToken")
public String getToken(HttpServletRequest request, HttpServletResponse response, Model model) {
// jsonObject
PrintWriter out = response.getWriter();
out.print(jsonObject);
}
Now, A user can make a GET Request to above mapping using a url like this:
localhost:8080/getToken?username="username"&password="password"
I have also created a class called CORSFilter that implements javax.servlet.Filter and i want this filter to intercept only those request that have /getToken in the request path.
#WebFilter(urlPatterns = "/getToken")
public class CORSFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//validate user and password
chain.doFilter(requestToUse, responseToUse);
}
}
Now, when I hit localhost:8080 or localhost:8080/thankyou in the browser then the above filter is getting called.
How can I stop this? I want to call above filter only if the url path is localhost:8080/getToken?username="user"&password="pass"
Unfortunately, Spring Boot doesn't honor the urlPatterns for filters declared with WebFilter. If you need to apply a pattern, you'll have to declare the filter as a bean and declare a filter registration for that bean, in a configuration class. For example:
#Bean
public FilterRegistrationBean<CORSFilter> corsFilterRegistration() {
FilterRegistrationBean<CORSFilter> filterRegistrationBean =
new FilterRegistrationBean<>(corsFilter());
filterRegistrationBean.setUrlPatterns(Collections.singleton("/getToken"));
return filterRegistrationBean;
}
#Bean
public CORSFilter corsFilter() {
return new CORSFilter();
}
Note however that Spring Boot has native support for CORS. You don't need any filter for that.
You can use interceptors- HandlerInterceptor. Please refer below url for how-to.
https://www.journaldev.com/2676/spring-mvc-interceptor-example-handlerinterceptor-handlerinterceptoradapter
If it is pure jsp based application then you need to declare it in web.xml because #WebFilter doesn't guarantee the order of execution of filters or if it is spring based application then you can use #CrossOrigin on method level or declare a bean to filter each and every url as mentioned in the below url
Spring CORS
I'm trying to implement a ContainerRequestFilter that checks some stuff. Ultimately, it shall extract the common name from a SSL client certificate, but I'm not there yet. The filter runs on a Grizzly HTTP Server (Grizzly 2.3.8, no servlet container) and sits in front of a JAX-RS resource (Jersey 2.6).
When I'm trying to inject org.glassfish.grizzly.http.server.Request into the filter, it is null.
import org.glassfish.grizzly.http.server.Request;
#Provider
#Priority(Priorities.AUTHENTICATION) // somewhat early
#SkpSecureClient // name the filter with my own annotation
public class SecureClientFilter implements ContainerRequestFilter {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Context
Request request;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (request == null) {
log.debug("Unable to inject 'request'");
// I always end up here
} else if (request.isSecure()) {
log.warn("Request is not secure!");
// do something, e.g. abort request
} else {
log.debug("Request is secure!");
}
}
}
Injecting the request into a JAX-RS resource, the injection is successful and I can work with it.
What am I doing wrong?
I think it's a bug in Jersey.
Just filed an issue
https://java.net/jira/browse/JERSEY-2462