I implemented rest web services with Spring. When I deployed it in Eclipse as a Spring Boot Application, it works. However when I deployed it in Tomcat 7 on the same machine, it does not work. The error message is as follows:
XMLHttpRequest cannot load http://localhost:8080/ristoreService/oauth/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8081' is therefore not allowed access.
My CORS filter looks like this:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8081");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me, "
+ "Origin,Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
If I comment out response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8081");, I still get the same error. It wouldn't work without this line even if I deploy in Eclipse. Why does it act differently being deployed under different environment on the same ip?
EDIT:
I tested the url http://localhost:8080/ristoreService/oauth/tokenwith rest client tester "CocoaRestClient" and got 404. So I made up a url which apparently does not exist http://localhost:8080/xxxxx and run it in UI (angularjs) and again got the CORS error. I think the error is kind of misleading, it is after all a 404. But why does it complain not found when the war was deployed successfully with the name ristoreService.war under webapps in Tomcat?
Try using a FilterRegistrationBean. Looks like this in Java Config:
#Bean
public FilterRegistrationBean authorizationFilter(){
FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(authorizationFilter);
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add("/v1/*");
filterRegBean.setUrlPatterns(urlPatterns);
return filterRegBean;
}
Any reason why you're not using Spring Boot's CORS capabilities? It's already supported out of the box, you just gotta configure it. You can enable it globally like this:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
According to How to deploy Spring Boot application, I have to make main application to extend SpringBootServletInitializer. Once I added that, it works.
To solve CORS issue I used #CrossOrigin. And I did not implement my own CORS filter. Any way spring already have provided few addition solutions for CORS issue.
If you need only your filter you could use it in this way:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(yourFilter);
...
}
Related
We need to set below response headers in CQ5/AEM based application.
Http Header: Frame options Set X-Frame-Options: SAME ORIGIN
Http Header: Strict Transport Security Strict-Transport-Security: max-age=10886400; includeSubDomains; preload
I have done like these changes in my prior application using servlet filter to intercept the request. Similarly I tried to use the SlingFilter for this application. However it seems to be called many times for each request (when it download js,css,json files etc). Please see below code snippet,
#SlingFilter(scope = SlingFilterScope.COMPONENT, order = Integer.MIN_VALUE)
#Properties({
#Property(name="pattern",value="/soni/template/", propertyPrivate=false)
})
public class ResponseHeaderFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
try {
HttpServletResponse httpResponse = (HttpServletResponse)response;
httpResponse.setHeader("Strict-Transport-Security", "max-age=10886400; includeSubDomains");
filterChain.doFilter(request, httpResponse);
httpResponse.setHeader("X-Frame-Options", "SAMEORIGIN");
} catch (Exception e) {
e.printStackTrace();
}
}
Is this the right way to do it in CQ5/AEM? How do I restrict this to once per page request?
or is there better way to do it like configuration at dispatcher etc?
Because you are using the Component scope, this filter is called every time you include a component, which is not your intention.
Please use request scope:
scope = SlingFilterScope.REQUEST
Refer to this page:
https://sling.apache.org/documentation/the-sling-engine/filters.html
I am trying to access following sling servlet using http://localhost:4502/sling/test-services/planet.html
But, it is giving 404 error, not sure what I am doing wrong here.
#Component
#Service(value=javax.servlet.Servlet.class)
#Properties({
#Property(name="service.description", value="HTML renderer for Planet resources"),
#Property(name="service.vendor", value="The Apache Software Foundation"),
#Property(name="sling.servlet.resourceTypes", value="sling/test-services/planet"),
#Property(name="sling.servlet.extensions", value="html"),
#Property(name="sling.servlet.methods", value="GET")
})
#SuppressWarnings("serial")
public class PlanetResourceRenderingServlet extends SlingSafeMethodsServlet {
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
final ValueMap properties = request.getResource().adaptTo(ValueMap.class);
// TODO should escape output - good enough for our tests
final PrintWriter pw = response.getWriter();
pw.println(String.format("<html><head><title>Planet at %s</title></head><body>", request.getResource().getPath()));
pw.println(String.format("<p>Name: %s</p>", properties.get("name")));
pw.println(String.format("<p>Distance: %s</p>", properties.get("distance")));
pw.println("</body></html>");
pw.flush();
}
}
Is it possible, I could access the servlet service without ".html" extension, if I remove extension property?
I appreciate any help.
Thank you!
When you want to access a servlet through an URL you need to set the sling.servlet.paths instead of the sling.servlet.resourceTypes. A similar issue has been answered here.
If you are setting the sling.servlet.resourceTypes property, then you need to access a resource whose sling:resourceType is sling/test-services/planet.
Your annotations should be
#Component
#Service(value=javax.servlet.Servlet.class)
#Properties({
#Property(name="service.description", value="HTML renderer for Planet resources"),
#Property(name="service.vendor", value="The Apache Software Foundation"),
#Property(name="sling.servlet.paths", value="/sling/test-services/planet"),
#Property(name="sling.servlet.extensions", value="html"),
#Property(name="sling.servlet.methods", value="GET")
})
Or this can be further simplified using the #SlingServlet annotation as shown below
#SlingServlet(paths="/sling/test-services/planet", methods="GET", extensions="html")
Make sure that you allow the following path is allowed in Apache Sling Servlet/Script Resolver and Error Handler configuration available in OSGi console.
I have implementated a Rest web service (the function is not relevant) using JAX-RS. Now I want to generate its documentation using Swagger. I have followed these steps:
1) In build.gradle I get all the dependencies I need:
compile 'org.glassfish.jersey.media:jersey-media-moxy:2.13'
2) I documentate my code with Swagger annotations
3) I hook up Swagger in my Application subclass:
public class ApplicationConfig extends ResourceConfig {
/**
* Main constructor
* #param addressBook a provided address book
*/
public ApplicationConfig(final AddressBook addressBook) {
register(AddressBookService.class);
register(MOXyJsonProvider.class);
register(new AbstractBinder() {
#Override
protected void configure() {
bind(addressBook).to(AddressBook.class);
}
});
register(io.swagger.jaxrs.listing.ApiListingResource.class);
register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8282");
beanConfig.setBasePath("/");
beanConfig.setResourcePackage("rest.addressbook");
beanConfig.setScan(true);
}
}
However, when going to my service in http://localhost:8282/swagger.json, I get this output.
You can check my public repo here.
It's times like this (when there is no real explanation for the problem) that I throw in an ExceptionMapper<Throwable>. Often with server related exceptions, there are no mappers to handle the exception, so it bubbles up to the container and we get a useless 500 status code and maybe some useless message from the server (as you are seeing from Grizzly).
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
public class DebugMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) {
exception.printStackTrace();
if (exception instanceof WebApplicationException) {
return ((WebApplicationException)exception).getResponse();
}
return Response.serverError().entity(exception.getMessage()).build();
}
}
Then just register with the application
public ApplicationConfig(final AddressBook addressBook) {
...
register(DebugMapper.class);
}
When you run the application again and try to hit the endpoint, you will now see a stacktrace with the cause of the exception
java.lang.NullPointerException
at io.swagger.jaxrs.listing.ApiListingResource.getListingJson(ApiListingResource.java:90)
If you look at the source code for ApiListingResource.java:90, you will see
Swagger swagger = (Swagger) context.getAttribute("swagger");
The only thing here that could cause the NPE is the context, which scrolling up will show you it's the ServletContext. Now here's the reason it's null. In order for there to even be a ServletContext, the app needs to be run in a Servlet environment. But look at your set up:
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(uri, new ApplicationConfig(ab));
This does not create a Servlet container. It only creates an HTTP server. You have the dependency required to create the Servlet container (jersey-container-grizzly2-servlet), but you just need to make use of it. So instead of the previous configuration, you should do
ServletContainer sc = new ServletContainer(new ApplicationConfig(ab));
HttpServer server = GrizzlyWebContainerFactory.create(uri, sc, null, null);
// you will need to catch IOException or add a throws clause
See the API for GrizzlyWebContainerFactory for other configuration options.
Now if you run it and hit the endpoint again, you will see the Swagger JSON. Do note that the response from the endpoint is only the JSON, it is not the documentation interface. For that you need to use the Swagger UI that can interpret the JSON.
Thanks for the MCVE project BTW.
Swagger fixed this issue in 1.5.7. It was Issue 1103, but the fix was rolled in last February. peeskillet's answer will still work, but so will OP's now.
I have a Spring Boot web app that runs just fine from STS but shows different behavior when running in Tomcat from a WAR file.
I use Thymeleaf to handle all my web pages but I have a couple pages that are using jQuery to send async calls and make user experience more dynamic.
Anyway, I have a Controller method that calls a service method which may throw a RuntimeException which I handle this way :
#ExceptionHandler(MyRuntimeException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public #ResponseBody String handleMyRuntimeException(MyRuntimeException exception) {
return "Oops an error happened : " + exception.getMessage();
}
In JS, I use the response body I return above to display a message on screen.
That works perfectly fine when running my app in STS but once I switch to deploy it in Tomcat the ErrorPageFilter gets invoked and in doFilter() it does:
if (status >= 400) {
handleErrorStatus(request, response, status, wrapped.getMessage());
response.flushBuffer();
}
In handleErrorStatus() it creates an error with the status and associated message but doesn't return my response.
I haven't figured out how to solve this and I'd really appreciate if anybody could help.
Thanks!
I went around this issue (I would think that is a Spring Boot issue) by doing the following.
Separate Rest and Mvc controllers
See my question here : Spring MVC: Get i18n message for reason in #RequestStatus on a #ExceptionHandler
Inject Jackson converter and write response myself :
#ControllerAdvice(annotations = RestController.class)
#Priority(1)
#ResponseBody
public class RestControllerAdvice {
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#ExceptionHandler(RuntimeException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public void handleRuntimeException(HttpServletRequest request, HttpServletResponse response, RuntimeException exception) {
try {
jacksonMessageConverter.write(new MyRestResult(translateMessage(exception)), MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
response.flushBuffer(); // Flush to commit the response
} catch (IOException e) {
e.printStackTrace();
}
}
}
I am using Spring session and Spring security for my REST API, but encountered a problem when I enabled CORS via a simple filter.
If I used a relative URI by http proxy(map http://xxxx/api to /api in client app), it works well.
If I used the full URL directly, I encountered a problem when used CORS, it can not fetch the session info, the following is the Spring security log.
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.FilterChainProxy - /api/mgt/appupdates at position 1 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.FilterChainProxy - /api/mgt/appupdates at position 2 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
I am using the Spring stack, including Spring 4.1.4.RELEASE, Spring Security 4, Spring Session 1.0.0.RELEASE, etc
Spring session config:
#Configuration
#EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60 * 120 )
public class RedisHttpSessionConfig {
#Bean
public HttpSessionStrategy httpSessionStrategy(){
return new HeaderHttpSessionStrategy();
}
}
The Http Session initializer class content:
#Order(100)
public class RedisHttpSessionApplicationInitializer
extends AbstractHttpSessionApplicationInitializer {}
The RedisHttpSessionConfig is loaded in my web initializer(#Order(0)). And there is another Initializer for Spring Security(#Order(200)).
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
private static final Logger log = LoggerFactory.getLogger(SecurityInitializer.class);
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", DelegatingFilterProxy.class);
corsFilter.addMappingForUrlPatterns(
EnumSet.of(
DispatcherType.ERROR,
DispatcherType.REQUEST,
DispatcherType.FORWARD,
DispatcherType.INCLUDE,
DispatcherType.ASYNC),
false,
"/*"
);
I have resolved the problem. I moved the doFilter method into a else block.
#Named("corsFilter")
public class SimpleCorsFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(SimpleCorsFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (log.isDebugEnabled()) {
log.debug("call doFilter in SimpleCORSFilter #");
}
response.setHeader("Access-Control-Allow-Origin", "*");
// response.addHeader("X-FRAME-OPTIONS", "SAMEORIGIN");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
if (log.isDebugEnabled()) {
log.debug("do pre flight...");
}
response.setHeader("Access-Control-Allow-Methods", "POST,GET,HEAD,OPTIONS,PUT,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Content-Type,Accept,x-auth-token,x-xsrf-token,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Access-Control-Allow-Origin");
//response.setHeader("Access-Control-Expose-Headers", "Access-Control-Allow-Origin,x-auth-token");
} else {
filterChain.doFilter(request, response);
}
}
}
Thus the doFilter only executes in none OPTIONS method. This solution overcomes this barrier temporarily. I think this could be a Spring Session related bug.
A CORS filter needs to know about the Spring Session header in the list of allowed request headers. HeaderHttpSessionStrategy defines this as "x-auth-token" but it can be changed. Note that any header starting with "x-" is treated as application specific, a tell-tale sign you need to configure your CORS filter to allow it.
May it add a #CrossOrigin to your request method help? like below:
#GetMapping("/path")
#CrossOrigin
public String detail(#PathVariable("id") Integer activityId){
return "";
}