Jump to content

Příručka:CORS

From mediawiki.org
This page is a translated version of the page Manual:CORS and the translation is 100% complete.

Tato stránka podrobně popisuje, jak používat požadavky CORS (sdílení zdrojů napříč různými doménami) ve vašem kódu JavaScript pro komunikaci mezi wikinami na různých doménách.

API MediaWiki, které zahrnuje Action API a REST API, podporuje následující typy požadavků CORS:

  • Ověřené požadavky pomocí souborů cookie, pokud je na vzdálené wiki povolena proměnná $wgCrossSiteAJAXdomains . Používá se na stránkách Wikimedie například k umožnění nahrávání obrázků přímo do Commons ze stránek Wikipedie v mobilním rozhraní.
    • Zahrnuje, ale není omezeno na, ověřené požadavky, ve kterých přihlášený uživatel na vzdálené wiki provádí akci jménem lokální wiki
  • Ověřené požadavky pomocí OAuth.
  • Anonymní, neověřené požadavky, používané hlavně pro omezenější akce, jako je načítání veřejně dostupných dat.

Konfigurace

Rozhraní Action API a REST API mají odlišné požadavky na zpracování požadavků CORS.

Action API

Pro požadavky na rozhraní Action API je CORS ve výchozím nastavení povoleno, ale adresa URL požadavku musí ve svém řetězci dotazu obsahovat buď parametr origin s příslušnou hodnotou nebo parametr crossorigin. Důvodem je, že požadavky POST CORS jsou preflighted (předběžně zkontrolováno) a parametr origin/crossorigin musí být zahrnut v požadavku na preflight.

  • Chcete-li povolit ověřené požadavky pomocí souborů cookie, použijte origin a ujistěte se, že hodnota odpovídá jedné z hodnot nastavených v $wgCrossSiteAJAXdomains na cizí wiki. Všimněte si, že hodnota parametru origin musí začínat protokolem HTTPS (např. https://mediawiki.org), i když $wgCrossSiteAJAXdomains akceptuje hodnoty bez něj.
  • Chcete-li povolit ověřené požadavky pomocí OAuth, použijte crossorigin (s libovolnou hodnotou) spolu s příslušnou hlavičkou požadavku Authorization. Pravděpodobně budete chtít použít OAuth 2 namísto OAuth 1.0a. Podrobnosti najdete na stránce OAuth/Pro vývojáře . Od MediaWiki 1.44change 1118583
  • Chcete-li povolit anonymní požadavky odkudkoli, nastavte parametr řetězce dotazu origin na hodnotu *, což je hvězdička.

REST API

Chcete-li povolit ověřené požadavky, můžete provést jednu ze dvou věcí:

  • Použít rozšíření OAuth (doporučený přístup). Rozhraní REST API bylo navrženo pro použití s ​​rozšířením OAuth pro ověřování a autorizaci uživatelů.
  • Nastavit $wgRestAllowCrossOriginCookieAuth na true, aby jakýkoli zdroj uvedený v $wgCrossSiteAJAXdomains mohl odesílat soubory relace cookie pro autorizaci v REST API.

Aby bylo možné povolit anonymní požadavky na REST API, musí být na vzdálené wiki nastaveno $wgAllowCrossOrigin na true. Tím se v záhlaví nastaví hodnota Access-Control-Allow-Origin na *. Na rozdíl od Action API nemá REST API v URL požadavku parametr origin.

Metody JavaScriptu

Použití mediawiki.ForeignApi

MediaWiki ResourceLoader nabízí modul mediawiki.ForeignApi, který je rozšířením modulu mediawiki.api a automaticky zpracovává podrobnosti za vás. Nabízí dva konstruktory pro povolení CORS:

  • Pro požadavky na Action API použijte mw.ForeignApi. (zavedeno od 1.26)
  • Pro požadavky na REST API použijte mw.ForeignRest. (zavedeno od 1.36)

Příklady jsou uvedeny níže. Pro použití mw.ForeignApi nebo mw.ForeignRest by rozšíření měla nastavit mediawiki.ForeignApi jako závislost modulu ResourceLoader v extension.json.

Pokud použijete hodnotu mw.ForeignApi() s POST požadavkem (.post()), pak se automaticky zahrne hodnota origin=*. Pokud potřebujete použít mw.ForeignApi() s požadavkem GET (.get()), ujistěte se, že origin=*, pokud je to nutné, je připojeno přímo k URL (ne k řetězci dotazu).

Pokud je pro požadovanou akci nutné, aby byl uživatel na cizí wiki přihlášen, předejte parametr assert: 'user' proměnné get()/post(). Chcete-li potvrdit, že uživatel na cizí wiki má konkrétní uživatelské jméno, předejte parametr assertuser s požadovaným uživatelským jménem.

Používání Fetch

Pokud lze požadavek GET provést anonymně, můžete také použít Fetch (moderní náhradu XMLHttpRequest založenou na Promise).

Použití jQuery.ajax

Pokud z jakéhokoli důvodu nechcete používat mediawiki.api, nebo pokud vás zajímá, jak to funguje na nižší úrovni, můžete stejnou funkcionalitu implementovat pomocí jednoduché jQuery AJAX. Můžete použít i obyčejné XMLHttpRequest. Příklady jsou uvedeny níže.

Pokud má aktuální uživatel zůstat přihlášený a potřebujete, aby prohlížeč používal soubory cookie, které pro danou doménu případně obsahuje, je také nutné nastavit pole withCredentials v atributu XMLHttpRequest na hodnotu true.

Příklady

V níže uvedených příkladech předpokládáme, že lokální wiki, ze které požadavky pocházejí, je www.mediawiki.org a že cizí wiki, na kterou požadavky cílí, je en.wikipedia.org.

Použití mw.ForeignApi

Ověřené požadavky

Příklad, který kontroluje, zda je uživatel přihlášen na cizí wiki:

await mw.loader.using( 'mediawiki.ForeignApi' )
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php', { userAgent: 'NameOfTool (ContactInformation)' } );
const data = await api.get( { action: 'query', meta: 'userinfo' } );
alert( `Foreign user ${data.query.userinfo.name} (ID ${data.query.userinfo.id})` );

Základní příklad zápisového API. Získáváme token csrf a používáme ho k nastavení trvalé vlastní uživatelské preference, kterou by gadget mohl později použít:

await mw.loader.using( 'mediawiki.ForeignApi' );
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php', { userAgent: 'NameOfTool (ContactInformation)' } );
const data = await api.get( { action: 'query', meta: 'tokens' } );
await api.post( {
    action: 'options',
    token: data.query.tokens.csrftoken,
    optionname: 'userjs-test',
    optionvalue: 'Hello world!'
} );

Stejný příklad lze stručněji přepsat pomocí některých pomocných metod mediawiki.api, které jsou k dispozici i pro ForeignApi:

await mw.loader.using( 'mediawiki.ForeignApi' );
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php', { userAgent: 'NameOfTool (ContactInformation)' } );
await api.postWithToken( 'options', {
    action: 'options',
    optionname: 'userjs-test',
    optionvalue: 'Hello world!'
} );

Anonymní žádosti

Pokud cílová wiki neakceptuje požadavky napříč zdroji, nebo pokud nepotřebujete provádět akce zápisu nebo čtení omezených informací a chcete se vyhnout režijním nákladům, můžete nastavit volbu anonymous konstruktoru mediawiki.ForeignApi:

await mw.loader.using( 'mediawiki.ForeignApi' )
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php', {
    anonymous: true,
    userAgent: 'NameOfTool (ContactInformation)'
} } );
...

Použití mw.ForeignRest

Příklad, který používá REST API k získání kódu html hlavní stránky:

var api = new mw.ForeignRest( 'https://commons.wikimedia.org/w/rest.php/v1' );
await api.get( '/page/Main_Page/html' );

Příklad, který dotazuje stránky, jejichž názvy začínají na "Test" z Wikimedia Commons (a poté zaznamenává výsledky do prohlížeče):

var value = "Test";

var actionApi = new mw.ForeignApi( 'https://commons.wikimedia.org/w/api.php' );
const api = new mw.ForeignRest(
 'https://commons.wikimedia.org/w/rest.php/v1',
 actionApi,
 { anonymous: true }
);
api.get( '/search/title', {
 limit: '10',
 q: `${ encodeURIComponent( value ) }`,
 origin: '*'
} )
.done( function ( data ) {
 console.log( data );
} );

Používání Fetch

Get

Získejte názvy prvních tří obrázků z Wikimedia Commons.
var apiEndpoint = "https://commons.wikimedia.org/w/api.php";
var params = "action=query&list=allimages&ailimit=3&format=json";

/**
 * Odešlete žádost o získání obrázků
 */
fetch( apiEndpoint + "?" + params + "&origin=*" )
.then(function(response){
    return response.json();
})
.then(function(response) {
    var allimages = response.query.allimages; // Zpracujte výstup, abyste získali názvy obrázků
    Object.keys(allimages).forEach(function(key) {
        console.log(allimages[key].name);
    });
});

Post

Upravte testovací sandbox Wikipedie pomocí tokenu OAuth 2
var apiEndpoint = 'https://test.wikipedia.org/w/api.php';
var params = {
	action: 'edit',
	title: 'Wikipedia:Sandbox',
	text: 'Hello World',
	summary: 'Hello World',
	format: 'json',
	formatversion: '2',
	crossorigin: ''
}
// Nahraďte skutečným tokenem OAuth 2
var oauthToken = "OAuthAccessToken";

/**
 * Získejte token CSRF
 */
var tokenQuery = {
	action: 'query',
	meta: 'tokens',
	format: 'json',
	formatversion: '2',
	crossorigin: ''
};
var queryURL = new URL(apiEndpoint);
queryURL.search = new URLSearchParams(tokenQuery);
fetch(queryURL, {method: 'GET', headers: {'Authorization': 'Bearer ' + oauthToken}})
	.then(function(response){return response.text()})
	.then(function(data){
		try {data=JSON.parse(data);} catch (e) {console.error(e);}
		params.token = data?.query?.tokens?.csrftoken;
		if (params.token) {
			/**
			 * Úprava příspěvku.
			 * Action API requires data be posted as application/x-www-form-urlencoded (URLSearchParams) or multipart/form-data, rather than application/json (T212988)
			 */
			var postBody = new URLSearchParams();
			queryURL = new URL(apiEndpoint);
			Object.keys(params).forEach( key => {
				if ( key == 'action' || key == 'origin' || key == 'crossorigin' ) {
					queryURL.searchParams.append(key, params[key]);
				} else {
					postBody.append(key, params[key]);
				}
			});
			fetch(queryURL, {method: 'POST', headers: {'Authorization': 'Bearer ' + oauthToken}, body: postBody})
				.then(function(response){return response.text()})
				.then(function(data){
					try {data=JSON.parse(data);} catch (e) {console.error(e);}
					var result = data?.edit?.result;
					if (result) {
						console.log(result);
					} else {
						console.error("Error posting edit!");
					}
				});
		} else {
			console.error("Error retrieving CSRF token!");
		}
	});

Použití jQuery.ajax

Příklad, který kontroluje, zda je uživatel přihlášen na cizí wiki:

const { query } = await $.ajax( {
    url: 'https://en.wikipedia.org/w/api.php',
    data: {
        action: 'query',
        meta: 'userinfo',
        format: 'json',
        origin: 'https://www.mediawiki.org'
    },
    xhrFields: {
        withCredentials: true
    },
    dataType: 'json'
} );

alert( `Foreign user ${query.userinfo.name} (ID ${query.userinfo.id})` );

Základní příklad zápisového API:

const { query } = await $.ajax( {
    url: 'https://en.wikipedia.org/w/api.php',
    data: {
        action: 'query',
        meta: 'tokens',
        format: 'json',
        origin: 'https://www.mediawiki.org'
    },
    xhrFields: {
        withCredentials: true
    },
    dataType: 'json'
} );

await $.ajax( {
    url: 'https://en.wikipedia.org/w/api.php?origin=https://www.mediawiki.org',
    method: 'POST',
    data: {
        action: 'options',
        format: 'json',
        token: query.tokens.csrftoken,
        optionname: 'userjs-test',
        optionvalue: 'Hello world!'
    },
    xhrFields: {
        withCredentials: true
    },
    dataType: 'json'
} );

Příklady ověřování OAuth

Verze MediaWiki:
1.44
Gerrit change 1118583

Následující dva externí příklady ukazují, jak použít OAuth 2 k vytváření ověřených požadavků CORS:

Rozšíření mechanismu

CentralAuth

Verze MediaWiki:
1.26

CentralAuth umožňuje vašemu kódu ověřit se na cizí wiki jako uživatel aktuálně přihlášený na lokální wiki pomocí tokenu centralauthtoken. To zaručuje, že pro akce na obou wikinách bude použit stejný přidružený účet, na rozdíl od běžného CORS (který vyžaduje, aby se uživatel předem přihlásil na cizí wiki).

Pokud má jak lokální, tak i cizí wiki nainstalovaný CentralAuth, mechanismus mediawiki.ForeignApi je bez problémů rozšířen, aby to za vás zvládl. Pokud si to implementujete sami, podívejte se na centralauthtoken, kde najdete pokyny, jak získat token (z lokální wiki) a předat ho požadavku (cizí wiki).

Alternativy ke CORS

Pro anonymní požadavky můžete místo toho použít formát JSONP. Toto je jednodušší, ale o něco méně bezpečné, protože načítá a spouští libovolný JavaScriptový kód z wiki, takže útočník, který ovládne web MediaWiki, má XSS vektor proti vzdálenému webu.

Související odkazy