OPTIONS method not request with credentials, 401 [duplicate] - javascript

I'm trying to enable CORS support in my WebAPI project, and if I enable Anonymous Authentication then everything works fine, but with Windows Auth + disabled anonymous authentication, the OPTIONS request sent always returns a 401 unauthorized response. The site requesting it is on the DOMAIN so should be able to make the call, is there any way to get around the issue without disabling Windows Authentication?

You can allow only OPTIONS verb for anonymous users.
<system.web>
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*"/>
<deny users="?" />
</authorization>
</system.web>
According W3C specifications, browser excludes user credentials from CORS preflight: https://dvcs.w3.org/hg/cors/raw-file/tip/Overview.html#preflight-request

Several years later, but through the answer from #dariusriggins and #lex-li I have managed to add the following code to my Global.asax:
public void Application_BeginRequest(object sender, EventArgs e)
{
string httpOrigin = Request.Params["HTTP_ORIGIN"];
if (httpOrigin == null) httpOrigin = "*";
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", httpOrigin);
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Token");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
if (Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.StatusCode = 200;
var httpApplication = sender as HttpApplication;
httpApplication.CompleteRequest();
}
}
the httpOrigin is actually looked up in a list of allowed hosts but that just complicated things. This means that all other requests are validated but options just returns.
Thanks for this question, I would have been lost without it!

From MS:
If you disable anonymous authentication, it’s by design that IIS would return a 401 to any request. If they have enabled Windows auth, the 401 response in that case would have a WWW-Authenticate header to allow the client to start an authentication handshake. The question then becomes whether the client that the customer is using can do Windows authentication or not.
Finally, it seems like there might be an underlying question about whether it’s possible or not to configure a URL such that anonymous access is allowed for one verb (OPTIONS, in this case), but require Windows authentication for other verbs. IIS does not support this through simple configuration. It might be possible to get this behavior by enabling both Anonymous and Windows authentication, setting ACLs on the content that deny access to the anonymous user, and then configuring the handler mapping for the URL in question so that it does not verify the existence of the file associated with the URL. But it would take some playing with it to confirm this.

The easiest way to fix this is to create a rewrite rule with the condition request_method = ^OPTIONS$. Then set the action to be a custom response, set that to 200 OK. Then all options requests will respond with 200 instead of 401. This will fix the CORS issue.
Of course you still need to make sure you have the correct cross origin request headers.
This will stop options requests (which dont have any credentials) responding with 401 when integrated auth is enabled.

The accepted answer is correct however I was troubleshooting a rest api with a "node with iisnode and npm cors module" setup for a while and was not comfortable with just enabling anonymous authentication for all users. Since its a node application the system.web tag does not do much. I ended up with the following addition to the web.config:
<system.webServer>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
<authorization>
<add accessType="Allow" verbs="OPTIONS" users="?" />
<add accessType="Deny" verbs="GET, PUT, POST, DELETE" users="?" />
</authorization>
</security>
</system.webServer>

Extending the answer provided by #dariusriggins. Check this post: Microsoft | Developer: Putting it all together – CORS tutorial
For IIS Configurations:
Authorization rule
Authorization stage (or Authorization event), we need to make sure we only allow the anonymous requests from CORS preflight and require all other incoming requests have authentication credentials supplied. We can achieve this through Authorization Rules. A default authorization rule granting all users access to the site is already in place and supplied by default by IIS. We will start by modifying this rule to only allow anonymous users, if they send requests that are using the OPTIONS http verb. Below is the target configuration in IIS for this authorization rule:
Edit Authorization rule

Related Question: IIS hijacks CORS Preflight OPTIONS request
Merging info from answers found in multiple places. If you need to enable CORS on a ASP.net page method with Windows Authentication on the intranet, this is what seems to work. Without the changes to web.config, this doesn't work.
You need to add this to Global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
string httpOrigin = HttpContext.Current.Request.Params["HTTP_ORIGIN"] ?? HttpContext.Current.Request.Params["ORIGIN"] ?? "*";
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", httpOrigin);
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Token");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.StatusCode = 200;
var httpApplication = sender as HttpApplication;
httpApplication.CompleteRequest();
}
}
And this to web.config
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*."
verb="*" type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>

I've run into same issue today due to bug in IE 10 and 11, I'm using ServiceStack instead of WebApi, but the approach can work for you as well.
Enabled Windows Integrated and Anonymous Authentication on IIS Web Site.
Have a series of filters on the ServiceStack Pipeline,
For handling Cors and OPTIONS request, On Options request, I add necessary headers and end the request,
Filter for checking includng HttpRequest is Authenticated?,
etc filter,
After passing through all the filters, it executes the service.
CorsFeature.cs
AuthenticateFilter
In my AppHost,
appHost.Plugins.Add(new CorsFeature());
appHost.RequestFilters.Add(AuthenticateFilter.Authenticate);
I have modified the CorsFeature to handle OptionsRequest's in addition to adding headers, Authenticate Filter to check for requests authenticated!

What worked for me (when working with AngularJS or JQuery) is to add withCredentials:true to each request on client:
$http.get("http://localhost:88/api/tests", {withCredentials :true})
And enabling CORS on server, this was done with Microsoft.Owin.Cors from nuget and adding it in Startup like below:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
References:
CORS on server (see step 11): http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
CORS on client: http://brockallen.com/2012/12/15/cors-and-windows-authentication/

I'm using Web API and OWIN and I tried every suggested solution but the only thing that worked was the following
//use it in your startup class
app.Use((context, next) =>
{
if (context.Request.Headers.Any(k => k.Key.Contains("Origin")) && context.Request.Method == "OPTIONS")
{
context.Response.StatusCode = 200;
context.Response.Headers.Add("Access-Control-Allow-Origin", new string[1] { "ALLOWED_ORIGIN" });
context.Response.Headers.Add("Access-Control-Allow-Headers", new string[4] { "Origin", "X-Requested-With", "Content-Type", "Accept" });
context.Response.Headers.Add("Access-Control-Allow-Methods", new string[5] { "GET", "POST", "PUT", "DELETE", "OPTIONS" });
context.Response.Headers.Add("Access-Control-Allow-Credentials", new string[1] { "true" });
return context.Response.WriteAsync("");
}
return next.Invoke();
});
//this is important! Without it, it didn't work (probably because the middleware was too late)
app.UseStageMarker(PipelineStage.Authenticate);
you need to insert this code somewhere in one of your OWIN startup classes.
It's important to call app.UseStageMarker(PipelineStage.Authenticate) because otherwise the preflight check failed.
Further infos for UseStageMarker -> https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-middleware-in-the-iis-integrated-pipeline
It's also important that you need to explicitly define the allowed headers. It will fail if you use * as a placeholder.
Maybe it helps somebody.

I understand this is an old question with several possible solutions (as well as more questions), but in case anyone else comes across this, IIS CORS 1.0 is available as of Nov '17:
https://blogs.iis.net/iisteam/introducing-iis-cors-1-0
https://learn.microsoft.com/en-us/iis/extensions/cors-module/cors-module-configuration-reference
You can download it through IIS Windows Platform Installer (WPI). This should resolve many of your CORS authentication issues. Enjoy!

This IIS extension (IIS CORS Module) helped me to solve the 401-Unauthorized preflight request to an IIS-hosted app with Windows Authentication enabled. After installing this module I did IISRESET and in the Web.config file of my web-application I added the following:
<configuration>
<configSections>
<!-- ... (configSections must be the first element!) -->
</configSections>
<system.webServer>
<cors enabled="true">
<add origin="http://localhost:3000" allowCredentials="true" maxAge="120">
<allowHeaders allowAllRequestedHeaders="true"/>
<!-- Added 'allowMethods' just in case. -->
<allowMethods>
<add method="HEAD"/>
<add method="GET"/>
<add method="POST"/>
<add method="PUT"/>
<add method="DELETE"/>
<add method="OPTIONS"/>
</allowMethods>
</add>
</cors>
</system.webServer>
</configuration>
Here you can find more information on how to configure the IIS CORS Module:
Getting started with the IIS CORS Module.

instal iis cors module on your iis.
in case of WebApi add :
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
}
add this lines to your web.config:
<configuration>
<system.webServer>
<cors enabled="true" failUnlistedOrigins="true">
<add origin="*">
<allowHeaders allowAllRequestedHeaders="true">
<add header="Access-Control-Allow-Origin" />
<add header="Access-Control-Allow-Headers" />
<add header="Access-Control-Allow-Methods" />
</allowHeaders>
</add>
</cors>
</system.webServer>
</configuration>
more info. in this article.
add xhrFields: {withCredentials: true} to your ajax call.

Enabling SupportCredentials on EnableCorsAttribute in WebApiConfig.cs did the trick for me:
public static void Register(HttpConfiguration config)
{
//enable cors request just from localhost:15136
var cors = new EnableCorsAttribute("http://localhost:15136", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
//other stuff
}
https://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api
Make sure you send credentials when calling from javascript ({withCredentials :true})

Related

Orchestrator Authentication not working with Javascript XHR request

I try to build a website where I can internally control our UiPath Orchestrator. We are using an on-premise Orchestrator.
The connection was firstly tested in Postman/curl:
curl --location --request POST '{{url}}/api/Account/Authenticate' \
--header 'Content-Type: application/json' \
--data-raw '{
"tenancyName": "{{tenantName}}",
"usernameOrEmailAddress": "{{usernameOrEmailAddress}}",
"password": "{{password}}"
}'
This gives me back the authtoken without any issue. Perfect.
Then I tried to implement that curl as XHR in Javascript:
var data = JSON.stringify({"tenancyName":"...","usernameOrEmailAddress":"...","password":"..."});
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://.../api/account/authenticate");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(data);
But Firefox and Chrome are trying to preflight. So I get a 404 statuscode back:
Firefox:
Chrome:
I'm confused now how to fix it. Actually it is obviously a CORS issue. So I tried to set:
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
on our IIS7 server. Without any change.
Also I tried to set this setting to allow everything on the Node.js server and on the XHR request. But 404 stays.
Then I tried using Chrome and Firefox Plugins to disable CORS. But 404 stays.
And again in Postman it works perfectly since the first try. So it just a CORS issue. But I want to let CORS enable, maybe just configure it in a way that specific server are allowed. How to do that?
One solution is to use the non-preflight request:
var data = "tenancyName=...&usernameOrEmailAddress=...&password=...";
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://url/api/account/authenticate");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(data);
As "Content-Type", "application/x-www-form-urlencoded" is one of the non-preflight we simply dodge the CORS mechanics with it. And for sure the data variable was changed so the JSON.stringify is gone now.
As the UiPath Orchestrator server is in a private Azure environment, this is not a huge security issue at all.
Anyway solutions that disable the CORS completely on the IIS7 server are still welcome.
Another solution is to disable cors on IIS10.
But be sure that this is only for testing and only kind of secure when you run it non-public! Later you should enable it again and restrict to your used domains.
First install the Cors module.
And the second step is adding this line to the web.config file of the IIS10 server:
<configuration>
<system.webServer>
<cors enabled="true" failUnlistedOrigins="true">
<add origin="*">
<allowHeaders allowAllRequestedHeaders="true" />
</add>
</cors>
</system.webServer>
</configuration>

Trying to upload slightly bigger files using XMLHttpRequest result in POST http://.... 404 (Not Found) and http://... blocked by CORS policy

I got a simple XMLHttpRequest that simply allow user to upload xml files from a webpage to a server. It is something like this
var formData = new FormData();
formData.append("thefile", file);
xmlhttprequest.send(formData);
and my rest server is something like this to receive the call
[HttpPost, Route("api/v1/workaroundtemplates/upload")]
public async Task<IHttpActionResult> ExecuteFileUpload()
{
IHttpActionResult res;
try
{
var httpRequest = HttpContext.Current.Request;
var requestedFiles = new List<System.IO.Stream>();
if (httpRequest.Files.Count > 0)
{
HttpFileCollection files = httpRequest.Files;
//do things
res = Ok();
}
else
res = BadRequest();
}
catch (Exception ex)
{
mLog.Error(ex, "Failed to execute action");
res = InternalServerError(ex);
}
return res;
}
This worked fine when I am uploading small files. However when I am uploading files that are slightly bigger like 50 MB I get the following error happening in my browser console and it looks as if the request never even made it to the server.
POST http://localhost:1062/api/v1/workaroundtemplates/upload 404 (Not Found)
folder-package-list:1 Access to XMLHttpRequest at 'http://localhost:1062/api/v1/workaroundtemplates/upload'
from origin 'http://localhost:1348' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
I tried to add this to my request header:
xmlhttprequest.setRequestHeader("Access-Control-Allow-Origin", "*");
xmlhttprequest.setRequestHeader("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
this did not work the same error still persists. What is puzzling to me is that smaller files upload just fine. It is only the files that are bigger have this issue.
Does anyone know the cause for this? I have the freedom of changing either server or client side to allow this to work.
I figured out what is going on it has nothing to do with CORS policy. It is basically my web service in the web.config did not have the setting
<configuration>
<system.web>
<httpRuntime maxRequestLength="1048576" />
</system.web>
</configuration>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>
However the error message thrown on the frontend of the page is extremly confusing and not helpful at all and led me onto the wrong path

ajax POST - The response had HTTP status code 403, headers are missing, always OPTIONS

I have a really strange problem with an ajax post. I have many POST-requests in my application and they usually work. On serverside I allow cross-origin requests, like its shown in the Web.config of my C# ASMX webservice:
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD, OPTIONS" />
<add name="Access-Control-Allow-Credentials" value="true" />
<add name="Access-Control-Allow-Headers" value="X-Requested-With, origin, content-type, accept" />
</customHeaders>
My application is something like a shop, where you can put items in the cart. the post request (which writes the order in the database) works well when there are less then 5 items in the cart. The request fails when there are 5 or more items in the cart and the following error appears:
> jquery-3.1.1.min.js:4 POST https://url/SP_WRITE_Sperrmuellanmeldung 403 (Forbidden)
send # jquery-3.1.1.min.js:4
ajax # jquery-3.1.1.min.js:4
SPWriteSperrmuellanmeldung # index.js:3470
(anonymous) # index.js:3380
dispatch # jquery-3.1.1.min.js:3
q.handle # jquery-3.1.1.min.js:3
>
>
> /#zsfg_sp_anmelden:1 Failed to load
> https://url/SP_WRITE_Sperrmuellanmeldung:
> No 'Access-Control-Allow-Origin' header is present on the requested
> resource. Origin 'https://sperrmuellportal.ks-weimar.de' is therefore
> not allowed access. The response had HTTP status code 403.
>
and this warning:
> jquery-3.1.1.min.js:4 Cross-Origin Read Blocking (CORB) blocked
> cross-origin response
> https://url/SP_WRITE_Sperrmuellanmeldung
> with MIME type text/html. See
> https://www.chromestatus.com/feature/5629709824032768 for more
> details.
(url is just a placeholder to the real domain)
It seems that suddenly the headers are missing like you can see in the following screenshot:
The same request with less than 5 items has all the necessary headers:
I realized the request method is always OPTIONS.
I use xml as exchange format. The request itself looks like this:
$.ajax({
type: "POST",
url: UrlToWebservice + "SP_WRITE_Sperrmuellanmeldung",
dataType: 'xml',
data: {
argef: argef.join(";"),
gef_menge: gef_menge.join(";"),
vornameStr: vornameStr,
nachnameStr: nachnameStr,
strasseStr: strasseStr,
hausnrStr: hausnrStr,
hnrzusatzStr: hnrzusatzStr,
dateStr: dateStr,
plzStr: plzStr,
ortStr: ortStr,
telStr: telStr,
mailStr: mailStr,
bemerkungStr: bemerkungStr,
arplz: arplz,
arstrasse: arstrasse,
annahme_art: annahme_art,
benachrichtigung: benachrichtigung,
arnutzererstellt: arnutzererstellt,
arems_typ: sp_arems_typ,
arnutzerweiterg: arnutzerweiterg,
arfraktion: localStorage.getItem("arfraktion")
},
//something more
This is so strange. Does anybody know how to fix it? It drives me crazy.
I tried the same request directly on the backend without error, so the backend is able to solve requests like that.

How to set the Origin Request Header

I want to send an AJAX request to a remote API.
function Gettest(CourseID) {
var param = { "CourseID": CourseID};
param = JSON.stringify(param);
$.ajax({
type: "Post",
url: apiurl + "Course/DelCoursetargetedaudience",
contentType: "application/json; charset=utf-8",
data: param,
dataType: "json",
success: function (data) {
},
error: function (response) {
}
});
};
But I need to change the origin name, before sending the request.
Please refer to the image below.
In short: you cannot.
As described on MDN; Origin is a 'forbidden' header, meaning that you cannot change it programatically.
You would need to configure the web server to allow CORS requests.
To enable CORS, add this to your Web.config
<system.webServer>
<!-- Other stuff is usually located here as well... -->
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
<system.webServer>
Alternatively, in Global.asax.cs
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
/* Some register config stuff is already located here */
}
// Add this method:
protected void Application_BeginRequest()
{
HttpContext.Current.Response.AddHeader
(name: "Access-Control-Allow-Origin", value: "*");
}
}
Just as Baksteen stated, you cannot change this header value in JavaScript. You would have to edit your server configuration to allow cross origin requests.
But: After reading your comments, I think you need a solution for debugging and testing only.
In that case, you can use Chrome and start it with special unsafe parameters. If you provide this parameters to Chrome, it will allow you cross domain requests.
Do not use this chrome instance for other things than testing your page!
chrome --disable-web-security --user-data-dir
I tried several Add-ons for Firefox and Chrome, but they did not work with recent versions of the browsers. So I recommend to switch to chrome and use the parameters above to test your API calls.
If you are interested in a more powerful solution, you may want to use a Debugging Proxy, like Fiddler from Telerik. You may write a custom rule, so Fiddler changes your headers before the request leaves your PC. But you have to dig into the tool, before you can use all its powers. This may be interesting, because it may help you out on more than just this debugging issue.

JavaScript add CORS to jersey [duplicate]

I'm developing a java script client application, in server-side I need to handle CORS, all the services I had written in JAX-RS with JERSEY.
My code:
#CrossOriginResourceSharing(allowAllOrigins = true)
#GET
#Path("/readOthersCalendar")
#Produces("application/json")
public Response readOthersCalendar(String dataJson) throws Exception {
//my code. Edited by gimbal2 to fix formatting
return Response.status(status).entity(jsonResponse).header("Access-Control-Allow-Origin", "*").build();
}
As of now, i'm getting error No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.”
Please assist me with this.
Thanks & Regards
Buddha Puneeth
Note: Make sure to read the UPDATE at the bottom. The original answer includes a "lazy" implementation of the CORS filter
With Jersey, to handle CORS, you can just use a ContainerResponseFilter. The ContainerResponseFilter for Jersey 1.x and 2.x are a bit different. Since you haven't mentioned which version you're using, I'll post both. Make sure you use the correct one.
Jersey 2.x
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers",
"CSRF-Token, X-Requested-By, Authorization, Content-Type");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
If you use package scanning to discover providers and resources, the #Provider annotation should take care of the configuration for you. If not, then you will need to explicitly register it with the ResourceConfig or the Application subclass.
Sample code to explicitly register filter with the ResourceConfig:
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new CORSFilter());
final final URI uri = ...;
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);
For Jersey 2.x, if you are having problems registering this filter, here are a couple resources that might help
Registering Resources and Providers in Jersey 2
What exactly is the ResourceConfig class in Jersey 2?
Jersey 1.x
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
response.getHttpHeaders().add("Access-Control-Allow-Headers",
"CSRF-Token, X-Requested-By, Authorization, Content-Type");
response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHttpHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
return response;
}
}
web.xml configuration, you can use
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.CORSFilter</param-value>
</init-param>
Or ResourceConfig you can do
resourceConfig.getContainerResponseFilters().add(new CORSFilter());
Or package scanning with the #Provider annotation.
EDIT
Please note that the above example can be improved. You will need to know more about how CORS works. Please see here. For one, you will get the headers for all responses. This may not be desirable. You may just need to handle the preflight (or OPTIONS). If you want to see a better implemented CORS filter, you can check out the source code for the RESTeasy CorsFilter
UPDATE
So I decided to add a more correct implementation. The above implementation is lazy and adds all the CORS headers to all requests. The other mistake is that being that it is only a response filter, the request is still processes. This means that when the preflight request comes in, which is an OPTIONS request, there will be no OPTIONS method implemented, so we will get a 405 response, which is incorrect.
Here's how it should work. So there are two types of CORS requests: simple requests and preflight requests. For a simple request, the browser will send the actual request and add the Origin request header. The browser expects for the response to have the Access-Control-Allow-Origin header, saying that the origin from the Origin header is allowed. In order for it to be considered a "simple request", it must meet the following criteria:
Be one of the following method:
GET
HEAD
POST
Apart from headers automatically set by the browser, the request may only contain the following manually set headers:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Save-Data
Viewport-Width
Width
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
If the request doesn't meet all of these three criteria, a Preflight request is made. This is an OPTIONS request that is made to the server, prior to the actual request being made. It will contain different Access-Control-XX-XX headers, and the server should respond to those headers with its own CORS response headers. Here are the matching headers:
REQUEST HEADER
RESPONSE HEADER
Origin
Access-Control-Allow-Origin
Access-Control-Request-Headers
Access-Control-Allow-Headers
Access-Control-Request-Method
Access-Control-Allow-Methods
XHR.withCredentials
Access-Control-Allow-Credentials
With the Origin request header, the value will be the origin server domain, and the response Access-Control-Allow-Origin should be either this same address or * to specify that all origins are allowed.
If the client tries to manually set any headers not in the above list, then the browser will set the Access-Control-Request-Headers header, with the value being a list of all the headers the client is trying to set. The server should respond back with a Access-Control-Allow-Headers response header, with the value being a list of headers it allows.
The browser will also set the Access-Control-Request-Method request header, with the value being the HTTP method of the request. The server should respond with the Access-Control-Allow-Methods response header, with the value being a list of the methods it allows.
If the client uses the XHR.withCredentials, then the server should respond with the Access-Control-Allow-Credentials response header, with a value of true. Read more here.
So with all that said, here is a better implementation. Even though this is better than the above implementation, it is still inferior to the RESTEasy one I linked to, as this implementation still allows all origins. But this filter does a better job of adhering to the CORS spec than the above filter which just adds the CORS response headers to all request. Note that you may also need to modify the Access-Control-Allow-Headers to match the headers that your application will allow; you may want o either add or remove some headers from the list in this example.
#Provider
#PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
/**
* Method for ContainerRequestFilter.
*/
#Override
public void filter(ContainerRequestContext request) throws IOException {
// If it's a preflight request, we abort the request with
// a 200 status, and the CORS headers are added in the
// response filter method below.
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
/**
* A preflight request is an OPTIONS request
* with an Origin header.
*/
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
/**
* Method for ContainerResponseFilter.
*/
#Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
// if there is no Origin header, then it is not a
// cross origin request. We don't do anything.
if (request.getHeaderString("Origin") == null) {
return;
}
// If it is a preflight request, then we add all
// the CORS headers here.
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With, Authorization, " +
"Accept-Version, Content-MD5, CSRF-Token, Content-Type");
}
// Cross origin requests can be either simple requests
// or preflight request. We need to add this header
// to both type of requests. Only preflight requests
// need the previously added headers.
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
To learn more about CORS, I suggest reading the MDN docs on Cross-Origin Resource Sharing (CORS)
Remove annotation "#CrossOriginResourceSharing(allowAllOrigins = true)"
Then Return Response like below:
return Response.ok()
.entity(jsonResponse)
.header("Access-Control-Allow-Origin", "*")
.build();
But the jsonResponse should replace with a POJO Object!
The other answer might be strictly correct, but misleading. The missing part is that you can mix filters from different sources together. Even thought Jersey might not provide CORS filter (not a fact I checked but I trust the other answer on that), you can use tomcat's own CORS filter.
I am using it successfully with Jersey. I have my own implementation of Basic Authentication filter, for example, together with CORS. Best of all, CORS filter is configured in web XML, not in code.
peeskillet's answer is correct. But I get this error when refresh the web page (it is working only on first load):
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. Origin 'http://127.0.0.1:8080' is therefore not allowed access.
So instead of using add method to add headers for response, I using put method. This is my class
public class MCORSFilter implements ContainerResponseFilter {
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_VALUE = "*";
private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true";
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
public static final String ACCESS_CONTROL_ALLOW_HEADERS_VALUE = "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept";
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE, OPTIONS, HEAD";
public static final String[] ALL_HEADERs = {
ACCESS_CONTROL_ALLOW_ORIGIN,
ACCESS_CONTROL_ALLOW_CREDENTIALS,
ACCESS_CONTROL_ALLOW_HEADERS,
ACCESS_CONTROL_ALLOW_METHODS
};
public static final String[] ALL_HEADER_VALUEs = {
ACCESS_CONTROL_ALLOW_ORIGIN_VALUE,
ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE,
ACCESS_CONTROL_ALLOW_HEADERS_VALUE,
ACCESS_CONTROL_ALLOW_METHODS_VALUE
};
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
for (int i = 0; i < ALL_HEADERs.length; i++) {
ArrayList<Object> value = new ArrayList<>();
value.add(ALL_HEADER_VALUEs[i]);
response.getHttpHeaders().put(ALL_HEADERs[i], value); //using put method
}
return response;
}
}
And add this class to init-param in web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.MCORSFilter</param-value>
</init-param>
To solve this for my project I used Micheal's answer and arrived at this:
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<id>run-embedded</id>
<goals>
<goal>run</goal>
</goals>
<phase>pre-integration-test</phase>
<configuration>
<port>${maven.tomcat.port}</port>
<useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
<contextFile>${project.basedir}/tomcat/context.xml</contextFile>
<!--enable CORS for development purposes only. The web.xml file specified is a copy of
the auto generated web.xml with the additional CORS filter added -->
<tomcatWebXml>${maven.tomcat.web-xml.file}</tomcatWebXml>
</configuration>
</execution>
</executions>
</plugin>
The CORS filter being the basic example filter from the tomcat site.
Edit:
The maven.tomcat.web-xml.file variable is a pom defined property for the project and it contains the path to the web.xml file (located within my project)

Categories

Resources