Maison  >  Article  >  interface Web  >  Exemple de partage de code pour Node.js afin de configurer une liste blanche de noms multi-domaines dans les requêtes inter-domaines CORS

Exemple de partage de code pour Node.js afin de configurer une liste blanche de noms multi-domaines dans les requêtes inter-domaines CORS

黄舟
黄舟original
2017-03-28 14:34:182710parcourir

Cet article présente principalement la méthode de Node.js configuration d'une liste blanche de noms multi-domaines dans les requêtes inter-domaines CORS L'article le présente en détail à travers un exemple de code I. Je pense que cela sera utile à tout le monde. Il a une certaine valeur de référence. Les amis qui en ont besoin peuvent jeter un œil ci-dessous.

CORS

En parlant de CORS, je pense que tout le monde connaît le front-end, donc je n'entrerai pas dans les détails ici. pouvez lire cet article pour plus de détails.

CORS configure principalement l'attribut Access-Control-Allow-Origin dans l'en-tête Response sur le nom de domaine auquel vous autorisez l'interface à accéder. Les paramètres les plus courants sont :

res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true'); // 允许服务器端发送Cookie数据

Cependant, ce paramètre est le plus simple et le plus grossier, et c'est aussi le moins sûr. Cela signifie que l'interface permet à tous les noms de domaine de lui faire des requêtes inter-domaines. Cependant, dans les affaires réelles en général, on s'attend à ce que l'interface permette uniquement d'ouvrir des autorisations de requête inter-domaines à un ou quelques sites Web, pas à tous.

Alors, vous les gens intelligents, vous devez vous demander : n'est-il pas facile de mettre plusieurs noms de domaine sur liste blanche ? Il suffit d'écrire une règle régulière ? Si cela ne fonctionne pas, ne serait-il pas préférable de configurer directement l'attribut Access-Control-Allow-Origin sur plusieurs noms de domaine séparés par des virgules ?

Comme ceci :

res.header('Access-Control-Allow-Origin', '*.666.com'); 

// 或者如下
res.header('Access-Control-Allow-Origin', 'a.666.com,b.666.com,c.666.com');

J'ai le regret de vous dire que cette façon d'écrire est invalide. Dans Node.js, l'attribut Access-Control-Allow-Origin dans l'en-tête de réponse res ne peut pas correspondre aux expressions régulières autres que (*), et les noms de domaine ne peuvent pas être séparés par des virgules. En d'autres termes, la valeur de l'attribut Access-Control-Allow-Origin ne peut être définie que sur un seul nom de domaine spécifique chaîne ou (*).

Puisque nous souhaitons autoriser plusieurs noms de domaine et ne sommes pas disposés à utiliser le caractère générique non sécurisé * , est-il vraiment impossible de configurer CORS pour la liste blanche de plusieurs noms de domaine ?

CORS avec plusieurs listes blanches de noms de domaine est en effet réalisable. Il y a juste un petit truc pour sauver le pays.

Principe d'implémentation CORS de la liste blanche multi-domaines

Pour les principes spécifiques, veuillez vous référer au code de base de la bibliothèque cors :

(function () {

 'use strict';

 var assign = require('object-assign');
 var vary = require('vary');

 var defaults = {
 origin: '*',
 methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
 preflightContinue: false,
 optionsSuccessStatus: 204
 };

 function isString(s) {
 return typeof s === 'string' || s instanceof String;
 }

 function isOriginAllowed(origin, allowedOrigin) {
 if (Array.isArray(allowedOrigin)) {
 for (var i = 0; i < allowedOrigin.length; ++i) {
 if (isOriginAllowed(origin, allowedOrigin[i])) {
  return true;
 }
 }
 return false;
 } else if (isString(allowedOrigin)) {
 return origin === allowedOrigin;
 } else if (allowedOrigin instanceof RegExp) {
 return allowedOrigin.test(origin);
 } else {
 return !!allowedOrigin;
 }
 }

 function configureOrigin(options, req) {
 var requestOrigin = req.headers.origin,
 headers = [],
 isAllowed;

 if (!options.origin || options.origin === &#39;*&#39;) {
 // allow any origin
 headers.push([{
 key: &#39;Access-Control-Allow-Origin&#39;,
 value: &#39;*&#39;
 }]);
 } else if (isString(options.origin)) {
 // fixed origin
 headers.push([{
 key: &#39;Access-Control-Allow-Origin&#39;,
 value: options.origin
 }]);
 headers.push([{
 key: &#39;Vary&#39;,
 value: &#39;Origin&#39;
 }]);
 } else {
 isAllowed = isOriginAllowed(requestOrigin, options.origin);
 // reflect origin
 headers.push([{
 key: &#39;Access-Control-Allow-Origin&#39;,
 value: isAllowed ? requestOrigin : false
 }]);
 headers.push([{
 key: &#39;Vary&#39;,
 value: &#39;Origin&#39;
 }]);
 }

 return headers;
 }

 function configureMethods(options) {
 var methods = options.methods;
 if (methods.join) {
 methods = options.methods.join(&#39;,&#39;); // .methods is an array, so turn it into a string
 }
 return {
 key: &#39;Access-Control-Allow-Methods&#39;,
 value: methods
 };
 }

 function configureCredentials(options) {
 if (options.credentials === true) {
 return {
 key: &#39;Access-Control-Allow-Credentials&#39;,
 value: &#39;true&#39;
 };
 }
 return null;
 }

 function configureAllowedHeaders(options, req) {
 var allowedHeaders = options.allowedHeaders || options.headers;
 var headers = [];

 if (!allowedHeaders) {
 allowedHeaders = req.headers[&#39;access-control-request-headers&#39;]; // .headers wasn&#39;t specified, so reflect the request headers
 headers.push([{
 key: &#39;Vary&#39;,
 value: &#39;Access-Control-Request-Headers&#39;
 }]);
 } else if (allowedHeaders.join) {
 allowedHeaders = allowedHeaders.join(&#39;,&#39;); // .headers is an array, so turn it into a string
 }
 if (allowedHeaders && allowedHeaders.length) {
 headers.push([{
 key: &#39;Access-Control-Allow-Headers&#39;,
 value: allowedHeaders
 }]);
 }

 return headers;
 }

 function configureExposedHeaders(options) {
 var headers = options.exposedHeaders;
 if (!headers) {
 return null;
 } else if (headers.join) {
 headers = headers.join(&#39;,&#39;); // .headers is an array, so turn it into a string
 }
 if (headers && headers.length) {
 return {
 key: &#39;Access-Control-Expose-Headers&#39;,
 value: headers
 };
 }
 return null;
 }

 function configureMaxAge(options) {
 var maxAge = options.maxAge && options.maxAge.toString();
 if (maxAge && maxAge.length) {
 return {
 key: &#39;Access-Control-Max-Age&#39;,
 value: maxAge
 };
 }
 return null;
 }

 function applyHeaders(headers, res) {
 for (var i = 0, n = headers.length; i < n; i++) {
 var header = headers[i];
 if (header) {
 if (Array.isArray(header)) {
  applyHeaders(header, res);
 } else if (header.key === &#39;Vary&#39; && header.value) {
  vary(res, header.value);
 } else if (header.value) {
  res.setHeader(header.key, header.value);
 }
 }
 }
 }

 function cors(options, req, res, next) {
 var headers = [],
 method = req.method && req.method.toUpperCase && req.method.toUpperCase();

 if (method === &#39;OPTIONS&#39;) {
 // preflight
 headers.push(configureOrigin(options, req));
 headers.push(configureCredentials(options, req));
 headers.push(configureMethods(options, req));
 headers.push(configureAllowedHeaders(options, req));
 headers.push(configureMaxAge(options, req));
 headers.push(configureExposedHeaders(options, req));
 applyHeaders(headers, res);

 if (options.preflightContinue ) {
 next();
 } else {
 res.statusCode = options.optionsSuccessStatus || defaults.optionsSuccessStatus;
 res.end();
 }
 } else {
 // actual response
 headers.push(configureOrigin(options, req));
 headers.push(configureCredentials(options, req));
 headers.push(configureExposedHeaders(options, req));
 applyHeaders(headers, res);
 next();
 }
 }

 function middlewareWrapper(o) {
 if (typeof o !== &#39;function&#39;) {
 o = assign({}, defaults, o);
 }

 // if options are static (either via defaults or custom options passed in), wrap in a function
 var optionsCallback = null;
 if (typeof o === &#39;function&#39;) {
 optionsCallback = o;
 } else {
 optionsCallback = function (req, cb) {
 cb(null, o);
 };
 }

 return function corsMiddleware(req, res, next) {
 optionsCallback(req, function (err, options) {
 if (err) {
  next(err);
 } else {
  var originCallback = null;
  if (options.origin && typeof options.origin === &#39;function&#39;) {
  originCallback = options.origin;
  } else if (options.origin) {
  originCallback = function (origin, cb) {
  cb(null, options.origin);
  };
  }

  if (originCallback) {
  originCallback(req.headers.origin, function (err2, origin) {
  if (err2 || !origin) {
  next(err2);
  } else {
  var corsOptions = Object.create(options);
  corsOptions.origin = origin;
  cors(corsOptions, req, res, next);
  }
  });
  } else {
  next();
  }
 }
 });
 };
 }

 // can pass either an options hash, an options delegate, or nothing
 module.exports = middlewareWrapper;

}());

Le principe de mise en œuvre est le suivant :

Puisque l'attribut Access-Control-Allow-Origin a précisé que plusieurs noms de domaine ne peuvent pas être défini, alors nous devons abandonner ce chemin.

La méthode la plus populaire et la plus efficace consiste à déterminer côté serveur si la valeur de l'attribut Origin (req.header.origin) dans l'en-tête demandé figure dans notre liste blanche de noms de domaine. S'il figure dans la liste blanche, nous définissons Access-Control-Allow-Origin sur la valeur Origin actuelle, qui répond à l'exigence de nom de domaine unique d'Access-Control-Allow-Origin et garantit que la demande actuelle est accédée si c'est le cas ; n'est pas dans la liste blanche, un message d'erreur sera renvoyé.

De cette manière, nous transférons la vérification des requêtes cross-domaines du navigateur vers le serveur. La vérification de la chaîne Origin devient équivalente à la vérification d'une chaîne normale. Nous pouvons non seulement utiliser la vérification de tableau liste, mais également utiliser la correspondance régulière.

Le code spécifique est le suivant :

// 判断origin是否在域名白名单列表中
function isOriginAllowed(origin, allowedOrigin) {
 if (_.isArray(allowedOrigin)) {
 for(let i = 0; i < allowedOrigin.length; i++) {
  if(isOriginAllowed(origin, allowedOrigin[i])) {
  return true;
  }
 }
 return false;
 } else if (_.isString(allowedOrigin)) {
 return origin === allowedOrigin;
 } else if (allowedOrigin instanceof RegExp) {
 return allowedOrigin.test(origin);
 } else {
 return !!allowedOrigin;
 }
}


const ALLOW_ORIGIN = [ // 域名白名单
 &#39;*.233.666.com&#39;,
 &#39;hello.world.com&#39;,
 &#39;hello..*.com&#39;
];

app.post(&#39;a/b&#39;, function (req, res, next) {
 let reqOrigin = req.headers.origin; // request响应头的origin属性

 // 判断请求是否在域名白名单内
 if(isOriginAllowed(reqOrigin, ALLOW_ORIGIN)) {
 // 设置CORS为请求的Origin值
 res.header("Access-Control-Allow-Origin", reqOrigin);
 res.header(&#39;Access-Control-Allow-Credentials&#39;, &#39;true&#39;);

 // 你的业务代码逻辑代码 ...
 // ...
 } else {
 res.send({ code: -2, msg: &#39;非法请求&#39; });
 }
});

Oh ouais, c'est parfait~

Résumé

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn