Gibt es eine Möglichkeit, mehrere domänenübergreifende Domänen mit dem Access-Control-Allow-Origin
-Header zuzulassen?
Ich kenne den *
, aber er ist zu offen. Ich möchte nur ein paar Domains zulassen.
Zum Beispiel so etwas:
Access-Control-Allow-Origin: http://domain1.example, http://domain2.example
Ich habe den obigen Code ausprobiert, aber er scheint in Firefox nicht zu funktionieren.
Ist es möglich, mehrere Domänen anzugeben oder bin ich mit nur einer Domäne verbunden?
Anscheinend ist die empfohlene Methode, den Origin-Header vom Client auszulesen, mit der Liste der Domänen zu vergleichen, die Sie zulassen möchten, und den Wert des Origin
-Headers an den Client zurückzugeben als Access-Control-Allow-Origin
-Header in der Antwort.
Mit .htaccess
geht das so:
# ----------------------------------------------------------------------
# Allow loading of external fonts
# ----------------------------------------------------------------------
<FilesMatch "\.(ttf|otf|eot|woff)$">
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.example|dev02.otherdomain.example)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header merge Vary Origin
</IfModule>
</FilesMatch>
Eine andere Lösung, die ich in PHP verwende:
$http_Origin = $_SERVER['HTTP_Origin'];
if ($http_Origin == "http://www.domain1.com" || $http_Origin == "http://www.domain2.com" || $http_Origin == "http://www.domain3.com")
{
header("Access-Control-Allow-Origin: $http_Origin");
}
Das hat für mich funktioniert:
SetEnvIf Origin "^http(s)?://(.+\.)?(domain\.example|domain2\.example)$" Origin_is=$0
Header always set Access-Control-Allow-Origin %{Origin_is}e env=Origin_is
Wenn Sie .htaccess
eingeben, funktioniert es auf jeden Fall.
Ich hatte das gleiche Problem mit woff-Schriftarten, mehrere Subdomains mussten Zugriff haben. Um Subdomains zuzulassen, habe ich meiner httpd.conf so etwas hinzugefügt:
SetEnvIf Origin "^(.*\.example\.com)$" Origin_SUB_DOMAIN=$1
<FilesMatch "\.woff$">
Header set Access-Control-Allow-Origin "%{Origin_SUB_DOMAIN}e" env=Origin_SUB_DOMAIN
</FilesMatch>
Bei mehreren Domänen können Sie einfach den regulären Ausdruck in SetEnvIf
ändern.
So können Sie den Origin-Header zurückschicken, wenn er mit Nginx in Ihre Domäne passt. Dies ist nützlich, wenn Sie mehrere Subdomains einer Schriftart bereitstellen möchten:
location /fonts {
# this will echo back the Origin header
if ($http_Origin ~ "example.org$") {
add_header "Access-Control-Allow-Origin" $http_Origin;
}
}
Folgendes habe ich für eine PHP -Anwendung gemacht, die von AJAX angefordert wird
$request_headers = Apache_request_headers();
$http_Origin = $request_headers['Origin'];
$allowed_http_origins = array(
"http://myDumbDomain.example" ,
"http://anotherDumbDomain.example" ,
"http://localhost" ,
);
if (in_array($http_Origin, $allowed_http_origins)){
@header("Access-Control-Allow-Origin: " . $http_Origin);
}
Wenn der anfordernde Origin von meinem Server zugelassen wird, geben Sie $http_Origin
selbst als Wert des Access-Control-Allow-Origin
-Headers zurück, anstatt einen *
-Platzhalter zurückzugeben.
Es gibt einen Nachteil, den Sie beachten sollten: Sobald Sie Dateien an ein CDN (oder einen anderen Server, der kein Scripting zulässt) auslagern oder wenn Ihre Dateien auf einem Proxy zwischengespeichert werden, ändern Sie die Antwort basierend auf 'Origin' Anforderungsheader funktionieren nicht.
Für mehrere Domains in Ihrem .htaccess
:
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(domain1.example|domain2.example)$" AccessControlAllowOrigin=$0$1
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule>
Für Nginx-Benutzer, die CORS für mehrere Domänen zulassen. Ich mag das Beispiel des @ Marshalls, obwohl seine Antworten nur einer Domäne entsprechen. Um eine Liste von Domänen und Subdomänen abzugleichen, erleichtert diese Regex das Arbeiten mit Zeichensätzen:
location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
if ( $http_Origin ~* (https?://(.+\.)?(domain1|domain2|domain3)\.(?:me|co|com)$) ) {
add_header "Access-Control-Allow-Origin" "$http_Origin";
}
}
Dadurch werden nur die Header "Access-Control-Allow-Origin" wiedergegeben, die der angegebenen Domänenliste entsprechen.
Für IIS 7.5+ mit installiertem URL Rewrite 2.0-Modul siehe this SO Antwort .
Hier ist eine Lösung für Java-Web-App, basierend auf der Antwort von yesthatguy.
Ich verwende Jersey REST 1.x
Konfigurieren Sie die web.xml so, dass sie Jersey REST und den CORSResponseFilter kennt
<!-- Jersey REST config -->
<servlet>
<servlet-name>JAX-RS Servlet</servlet-name>
<servlet-class>com.Sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.Sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>com.Sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.your.package.CORSResponseFilter</param-value>
</init-param>
<init-param>
<param-name>com.Sun.jersey.config.property.packages</param-name>
<param-value>com.your.package</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS Servlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
Hier ist der Code für CORSResponseFilter
import com.Sun.jersey.spi.container.ContainerRequest;
import com.Sun.jersey.spi.container.ContainerResponse;
import com.Sun.jersey.spi.container.ContainerResponseFilter;
public class CORSResponseFilter implements ContainerResponseFilter{
@Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
String[] allowDomain = {"http://localhost:9000","https://my.domain.example"};
Set<String> allowedOrigins = new HashSet<String>(Arrays.asList (allowDomain));
String originHeader = request.getHeaderValue("Origin");
if(allowedOrigins.contains(originHeader)) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", originHeader);
response.getHttpHeaders().add("Access-Control-Allow-Headers",
"Origin, content-type, accept, authorization");
response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHttpHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
return response;
}
}
Wie oben erwähnt, sollte Access-Control-Allow-Origin
eindeutig sein und Vary
sollte auf Origin
gesetzt sein, wenn Sie sich hinter einem CDN (Content Delivery Network) befinden.
Relevanter Teil meiner Nginx-Konfiguration:
if ($http_Origin ~* (https?://.*\.mydomain.example(:[0-9]+)?)) {
set $cors "true";
}
if ($cors = "true") {
add_header 'Access-Control-Allow-Origin' "$http_Origin";
add_header 'X-Frame-Options' "ALLOW FROM $http_Origin";
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Vary' 'Origin';
}
Vielleicht bin ich falsch, aber soweit ich sehen kann, hat Access-Control-Allow-Origin
einen "Origin-list"
als Parameter.
Nach Definition ein Origin-list
lautet:
Origin = "Origin" ":" 1*WSP [ "null" / Origin-list ]
Origin-list = serialized-Origin *( 1*WSP serialized-Origin )
serialized-Origin = scheme "://" Host [ ":" port ]
; <scheme>, <Host>, <port> productions from RFC3986
Und ich behaupte, dass unterschiedliche Ursprünge zugelassen sind und Leerzeichen getrennt werden sollten.
Ich hatte Schwierigkeiten, dies für eine Domäne einzurichten, auf der HTTPS ausgeführt wird, also dachte ich, ich würde die Lösung teilen. Ich habe die folgende Direktive in meiner httpd.conf -Datei verwendet:
<FilesMatch "\.(ttf|otf|eot|woff)$">
SetEnvIf Origin "^http(s)?://(.+\.)?example\.com$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</FilesMatch>
Ändern Sie example.com
in Ihren Domainnamen. Fügen Sie dies in <VirtualHost x.x.x.x:xx>
in Ihre httpd.conf -Datei ein. Wenn Ihre VirtualHost
ein Port-Suffix hat (z. B. :80
), gilt diese Anweisung nicht für HTTPS. Daher müssen Sie auch nach/etc/Apache2/sites-available/default-ssl gehen und dieselbe hinzufügen Direktive in dieser Datei im Abschnitt <VirtualHost _default_:443>
.
Nachdem die Konfigurationsdateien aktualisiert wurden, müssen Sie die folgenden Befehle im Terminal ausführen:
a2enmod headers
Sudo service Apache2 reload
Wenn Sie Probleme mit Schriftarten haben, verwenden Sie:
<FilesMatch "\.(ttf|ttc|otf|eot|woff)$">
<IfModule mod_headers>
Header set Access-Control-Allow-Origin "*"
</IfModule>
</FilesMatch>
Ein flexiblerer Ansatz ist die Verwendung der Ausdrücke von Apache 2.4. Sie können mit Domains, Pfaden und fast jeder anderen Anforderungsvariablen übereinstimmen. Die Antwort ist zwar *
für alle, aber die einzigen Anforderer, die diese Antwort erhalten, sind diejenigen, die die Anforderungen trotzdem erfüllen.
<IfModule mod_headers.c>
<If "%{HTTP:Host} =~ /\\bcdndomain\\.example$/i && %{HTTP:Origin} =~ /\\bmaindomain\\.example$/i">
Header set Access-Control-Allow-Origin "*"
</If>
</IfModule>
Für ExpressJS-Anwendungen können Sie Folgendes verwenden:
app.use(function(req, res, next) {
const corsWhitelist = [
'https://domain1.example',
'https://domain2.example',
'https://domain3.example'
];
if (corsWhitelist.indexOf(req.headers.Origin) !== -1) {
res.header('Access-Control-Allow-Origin', req.headers.Origin);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
}
next();
});
Hier ist eine erweiterte Option für Apache, die einige der neuesten und geplanten Schriftdefinitionen enthält:
<FilesMatch "\.(ttf|otf|eot|woff|woff2|sfnt|svg)$">
<IfModule mod_headers.c>
SetEnvIf Origin "^http(s)?://(.+\.)?(domainname1|domainname2|domainname3)\.(?:com|net|org)$" AccessControlAllowOrigin=$0$1$2
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule>
</FilesMatch>
Für ein relativ einfaches Kopieren/Einfügen für .NET-Anwendungen habe ich dieses geschrieben, um CORS in einer global.asax-Datei zu aktivieren. Dieser Code folgt den Hinweisen in der aktuell akzeptierten Antwort und spiegelt wider, was Origin in der Anfrage in der Antwort zurückgibt. Dies erreicht effektiv "*", ohne es zu verwenden. Der Grund dafür ist, dass es mehrere andere CORS-Funktionen ermöglicht, einschließlich der Möglichkeit, AJAX XMLHttpRequest mit dem Attribut 'withCredentials' auf 'true' zu senden.
void Application_BeginRequest(object sender, EventArgs e)
{
if (Request.HttpMethod == "OPTIONS")
{
Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
Response.AddHeader("Access-Control-Max-Age", "1728000");
Response.End();
}
else
{
Response.AddHeader("Access-Control-Allow-Credentials", "true");
if (Request.Headers["Origin"] != null)
Response.AddHeader("Access-Control-Allow-Origin" , Request.Headers["Origin"]);
else
Response.AddHeader("Access-Control-Allow-Origin" , "*");
}
}
Und noch eine Antwort in Django. Um eine einzige Ansicht zu erhalten, die CORS aus mehreren Domänen zulässt, ist hier mein Code:
def my_view(request):
if 'HTTP_Origin' in request.META.keys() and request.META['HTTP_Origin'] in ['http://allowed-unsecure-domain.com', 'https://allowed-secure-domain.com', ...]:
response = my_view_response() # Create your desired response data: JsonResponse, HttpResponse...
# Then add CORS headers for access from delivery
response["Access-Control-Allow-Origin"] = request.META['HTTP_Origin']
response["Access-Control-Allow-Methods"] = "GET" # "GET, POST, PUT, DELETE, OPTIONS, HEAD"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"
return response
PHP Codebeispiel für übereinstimmende Subdomains.
if( preg_match("/http:\/\/(.*?)\.yourdomain.example/", $_SERVER['HTTP_Origin'], $matches )) {
$theMatch = $matches[0];
header('Access-Control-Allow-Origin: ' . $theMatch);
}
HTTP_Origin wird nicht von allen Browsern verwendet. Wie sicher ist HTTP_ORIGIN? Für mich kommt es leer in FF.
Ich habe die Websites, die ich für den Zugriff auf meine Site zulasse, über eine Site-ID senden. Dann überprüfe ich meine Datenbank auf den Datensatz mit dieser ID und erhalte den Spaltenwert SITE_URL (www.yoursite.com).
header('Access-Control-Allow-Origin: http://'.$row['SITE_URL']);
Selbst wenn das Senden über eine gültige Standort-ID erfolgt, muss die Anforderung von der Domäne stammen, die in meiner DB aufgeführt ist, die dieser Standort-ID zugeordnet ist.
Um den Zugriff auf mehrere Domänen für einen ASMX-Dienst zu erleichtern, habe ich diese Funktion in der Datei global.asax erstellt:
protected void Application_BeginRequest(object sender, EventArgs e)
{
string CORSServices = "/account.asmx|/account2.asmx";
if (CORSServices.IndexOf(HttpContext.Current.Request.Url.AbsolutePath) > -1)
{
string allowedDomains = "http://xxx.yyy.example|http://aaa.bbb.example";
if(allowedDomains.IndexOf(HttpContext.Current.Request.Headers["Origin"]) > -1)
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Headers["Origin"]);
if(HttpContext.Current.Request.HttpMethod == "OPTIONS")
HttpContext.Current.Response.End();
}
}
Dies ermöglicht auch die CORS-Behandlung von OPTIONS
Verb.
Die folgende Antwort bezieht sich speziell auf C #, das Konzept sollte jedoch auf alle verschiedenen Plattformen anwendbar sein.
Um Cross-Origin-Anforderungen von einer Web-API zuzulassen, müssen Sie Ihrer Anwendung Optionsanforderungen und der Anmerkung "Hinzufügen" auf Controller-Ebene hinzufügen.
[EnableCors (UrlString, Header, Methode)] Nun können die Ursprünge nur noch als Zeichenfolge übergeben werden. SO Wenn Sie mehr als eine URL in der Anfrage übergeben möchten, übergeben Sie diese als durch Kommas getrennten Wert.
UrlString = " https: //a.hello.com ,https: //b.hello.com "
Die Support-Antwort von Google für das Bereitstellen von Anzeigen über SSL und die Grammatik im RFC selbst scheinen darauf hinzudeuten, dass Sie die URLs durch Leerzeichen trennen können. Sie sind sich nicht sicher, wie gut dies in verschiedenen Browsern unterstützt wird.
Wenn Sie so viele Codebeispiele wie mich ausprobieren, damit es mit CORS funktioniert, ist es erwähnenswert, dass Sie zuerst Ihren Cache leeren müssen, um zu prüfen, ob es tatsächlich funktioniert auf dem Server gelöscht (weil es noch in Ihrem Cache gespeichert ist).
Zum Beispiel CTRL + SHIFT + DEL in Google Chrome, um Ihren Cache zu löschen.
Dies hat mir geholfen, diesen Code zu verwenden, nachdem ich viele reine .htaccess
-Lösungen ausprobiert hatte, und dies schien der einzige zu sein, der funktioniert (zumindest für mich):
Header add Access-Control-Allow-Origin "http://google.com"
Header add Access-Control-Allow-Headers "authorization, Origin, user-token, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
<FilesMatch "\.(ttf|otf|eot|woff)$">
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.com|dev02.otherdomain.net)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</IfModule>
</FilesMatch>
Beachten Sie auch, dass es weit verbreitet ist, dass in vielen Lösungen Header set ...
eingegeben werden muss, es sich jedoch um Header add ...
handelt. Hoffe, das hilft jemandem, der seit einigen Stunden die gleichen Probleme hat wie ich.
Für den Header Access-Control-Allow-Origin kann nur ein einziger Origin angegeben werden. Sie können jedoch den Ursprung in Ihrer Antwort entsprechend der Anforderung festlegen. Vergessen Sie auch nicht, den Vary-Header zu setzen. In PHP würde ich Folgendes tun:
/**
* Enable CORS for the passed origins.
* Adds the Access-Control-Allow-Origin header to the response with the Origin that matched the one in the request.
* @param array $origins
* @return string|null returns the matched Origin or null
*/
function allowOrigins($origins)
{
$val = $_SERVER['HTTP_Origin'] ?? null;
if (in_array($val, $origins, true)) {
header('Access-Control-Allow-Origin: '.$val);
header('Vary: Origin');
return $val;
}
return null;
}
if (allowOrigins(['http://localhost', 'https://localhost'])) {
echo your response here, e.g. token
}