How to add custom HTTP Method/Verb to Spring-MVC using #RequestMapping - spring

I need to add a custom HTTP Method/Verb to a URI. I need something like this:
#RequestMapping(value = "/device", method = "CAPTURE")
In short, this request needs to work
curl -X CAPTURE http://localhost:4591/device
It is my understanding that #RequestMapping only accepts standardized HTTP Methods, so what options do I have to achieve this.

You can't do that. You must use standardized HTTP methods.
You can create custom endpoints without HTTP method, so, every request will be accepted. (Of course, following some rules, but, isn't the topic of the question I guess)
#RequestMapping(value = "/device")
Or, you can use HTTP headers to allow/block requests. You just need to create a filter in your application. Something 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 {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, CAPTURE");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
String captureHeader = request.getHeader("CAPTURE");
if (StringUtils.isEmpty(captureHeader)) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}else{
chain.doFilter(req, res);
}
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
I guess using filter you will be able to do what you trying to do.

Related

blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present, but it literally is. SpringBoot

ok i'm quite sure you guys know this error so let's just skip on my solution and the reasons of it not working, I have an Interceptor which is basically a filter, where I did this:
#Component
public class AllRequestInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if(request.getMethod().equalsIgnoreCase("options")) {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "*");
}
for(String header: response.getHeaderNames()) {
System.out.println(header);
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
the CORS preflight request actually enters the if block, I get the headers printed afterwards and they're all present, and yet that party-pooper says otherwise, why does it say otherwise?
I'm not sure if this is what you are looking for, but this worked for me.
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsInterceptor implements Filter{
public CorsInterceptor() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
CORS must be sent on all requests, not just on OPTIONS: CORS HEADERS present only on preflight or every request
Simply remove if(request.getMethod().equalsIgnoreCase("options")) and its corresponding brackets, and it should work.

Cors error 'No Access-Control-Allow-Origin' works on localhost but not on server

in my spring boot application i have a custom Filter which sets the cors related headers. This works perfectly on localhost, my requests are going through my custom Filter and the headers are filled out.
But when I deploy my application to a server then I get an error, and it seems the preflight request doesn't call my custom Filter.
This is my Filter
#Component
public class CustomAllowAllCorsFilter implements Filter {
final private static Logger logger = LoggerFactory.getLogger(CustomAllowAllCorsFilter.class);
private boolean allowAllOrigins;
public CustomAllowAllCorsFilter() {
logger.info("init filter corsssss");
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
logger.info("doFilter - allowAllOrigins: " + allowAllOrigins);
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (allowAllOrigins) {
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
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",
"Authorization, Content-Type, Accept, X-Requested-With, remember-me, OCTO-Security-Type, OCTO-Security-Name, OCTO-Security-Token, OCTO-Security-Business");
}
if (request.getMethod().equals("OPTIONS"))
response.setStatus(HttpServletResponse.SC_OK);
else
filterChain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
#Value("${cors.allow-all-origins}")
public void setAllowAllOrigins(boolean allowAllOrigins) {
this.allowAllOrigins = allowAllOrigins;
}
}
And this is the error:
Failed to load http://xyz:8080/app/invoices: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://xii.com' is therefore not allowed access.
Does somebody have an idea?

Spring CrossOrigin issue: No Access-Control-Allow-Origin Header is present on the requested resource returning 401 as HTTP response

I have an API which takes two header ACCESS_TOKEN and TIME_OF_CALL as well as input parameter as mentioned below.
#RequestMapping(value = "/pay/capture", method = RequestMethod.GET)
#ResponseBody
#CrossOrigin(allowedHeaders = "ACCESS_TOKEN,TIME_OF_CALL", origins = "*")
public ResponseEntity<?> fetchRazorPayDetails(#RequestParam("id") Long id)
{/***logics here*/}
hitting this API from local host is returning 401 saying
XMLHttpRequest cannot load http://soandsoURL , response to preflight doesn't pass access control. check No Access-Control-Allow-Origin Header is present on the requested resource. origin 'http://localhost:1234' is therefore not allowed access. The response had HTTP status code 401
even though i had clearly annotated with
#CrossOrigin(allowedHeaders = "ACCESS_TOKEN,TIME_OF_CALL", origins = "*")
to accept all origin and two of the mentioned header. Am I missing something?
or values to allowedHeaders can't be comma separated? Any help on this issue
will be appreciated.
You can add a custom filter as bellow :
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
Since the issue was with Options(unauthorised 401 was the error), PFA screenshot.
It was just because I was declaring/adding options with #CrossOrigin. earlier it was `
#RequestMapping(value = "/pay/capture", method = RequestMethod.GET)
#ResponseBody
#CrossOrigin(allowedHeaders = "ACCESS_TOKEN,TIME_OF_CALL", origins = "*")
public ResponseEntity<?> fetchRazorPayDetails(#RequestParam("id") Long id)
{/***logics here*/}`
I removed optional parameter from #CrossOrigin and simply kept as
#RequestMapping(value = "/pay/capture", method = RequestMethod.GET)
#ResponseBody
#CrossOrigin
public ResponseEntity<?> fetchRazorPayDetails(#RequestParam("id") Long id)
{/***logics here*/}
and it worked.
Do something like this in your custom filter class:
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = this.getAsHttpRequest(req);
HttpServletResponse httpResponse=this.getAsHttpResponse(res);
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, authorization, ACCESS_TOKEN, TIME_OF_CALL");
chain.doFilter(req, res);
}
private HttpServletResponse getAsHttpResponse(ServletResponse response)
{
if (!(response instanceof HttpServletResponse)) {
throw new RuntimeException("Expecting an HTTP request");
}
return (HttpServletResponse) response;
}
private HttpServletRequest getAsHttpRequest(ServletRequest request)
{
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("Expecting an HTTP request");
}
return (HttpServletRequest) request;
}
And In the WebSecurity class do like this. It will work
//To allow Pre-flight [OPTIONS] request from browser
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}

CORS error in Spring Oauth2

I'm using Spring security and Oauth2. But I'm new to Spring Oauth2, I Got the CORS error when front-end attends to access resource.
I'm using the below filter to allow other domains to access the resource:
#Component
#Order(Integer.MAX_VALUE)
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Credentials", "True");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
I wrote the below code to allow public resource in my SecurityConfiguration.java.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/social/facebook/**","/register/**","/public/**").permitAll().and()
.authorizeRequests().antMatchers("/user/**").hasRole("USER").and()
.exceptionHandling()
.accessDeniedPage("/login.jsp?authorization_error=true")
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable();
}
For Oauth2, the below codes is for protecting user's resource in my OAuth2ServerConfig.java.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/user/**")
.and()
.authorizeRequests()
.antMatchers("/user/**").access("#oauth2.hasScope('read')")
.regexMatchers(HttpMethod.DELETE, "/oauth/users/([^/].*?)/tokens/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')")
.regexMatchers(HttpMethod.GET, "/oauth/clients/([^/].*?)/users/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')")
.regexMatchers(HttpMethod.GET, "/oauth/clients/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')");
}
When I open the index.html file in the browser, like following:(Sorry I don't have at least 10 reputation to post images, so I paste links here)
http://i.stack.imgur.com/yQKJM.png
it successfully get the public data, that means other domains are allowed to access "/public/**" data.
But it failed to get "/user/**" data (protected by Oauth2). It gives me below error says "Cross-Origin Request Blocked".
http://i.stack.imgur.com/XIVx1.png
When I move the front-end files to the same domain of the Spring server. It works fine to get both "public" and "user" data as below:
http://i.stack.imgur.com/Q2n7F.png
The front-end and Back-end should be separated. But the CORS is blocked to access projected data. Can anyone give me some suggestions? Thanks very much. I'm guessing the filter is not working on Oauth2? still spend a lot of time on looking for solutions.
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {
#Override
public void init(FilterConfig fc) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "PATCH,POST,GET,OPTIONS,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, resp);
}
}
#Override
public void destroy() {
}
}
I added the headers to the endpoints
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.addInterceptor(new HandlerInterceptorAdapter() {
#Override
public boolean preHandle(HttpServletRequest hsr, HttpServletResponse rs, Object o) throws Exception {
rs.setHeader("Access-Control-Allow-Origin", "*");
rs.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
rs.setHeader("Access-Control-Max-Age", "3600");
rs.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
return true;
}
});
}
The OAuth filter was called first and throw an Exception which has been prevented from running your CORS filter.
you must add this annotation
#Order(Ordered.HIGHEST_PRECEDENCE)
to your CORS filter.

Add headers to oauth/token response (spring-security)

I would like to add custom headers to Oauth2 token response for my spring application. Specifically it involves CORS headers i.e. Access-Control-Allow-Origin... I have managed to add them to 401 responses but have no luck with 200 ones.
I have looked everywhere and debugged the project with no result. I have tried adding those headers through interceptor but response still does not contain them.
Any ideas?
I'm using Spring security with annotation configuration.
I have asked similar question here: Allow OPTIONS HTTP Method for oauth/token request where you can check my spring configuration.
Use this Cors Filter (or maybe it works if you add the last lines of my version to your version) and you don't have the problem you mention in you other linked post!
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
public SimpleCorsFilter() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
It turned out I was using wrong method in my interceptor
for anyone interested, my working code is as follows:
return new AuthorizationServerConfigurer() {
...
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
...
endpoints.addInterceptor(new HandlerInterceptorAdapter() {
#Override
public boolean preHandle(HttpServletRequest hsr, HttpServletResponse rs, Object o) throws Exception {
rs.setHeader("Access-Control-Allow-Origin", "*");
rs.setHeader("Access-Control-Allow-Methods", "GET");
rs.setHeader("Access-Control-Max-Age", "3600");
rs.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
return true;
}
});
}
}
}

Resources