この記事では主に Node.js CORS クロスドメインリクエストでマルチドメイン名のホワイトリストを設定する方法について、サンプルコードを通して詳しく紹介しています。必要な場合はフォローすることができます。一緒に見てみましょう。
CORS
CORS について言えば、フロントエンドについては誰もがよく知っていると思うので、ここでは詳しく説明しません。詳しくは、この記事を読んでください。
CORS は主に、インターフェイスにアクセスを許可するドメイン名に対する応答ヘッダーの Access-Control-Allow-Origin 属性を構成します。最も一般的な設定は次のとおりです:
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Credentials', 'true'); // 允许服务器端发送Cookie数据
ただし、この設定は最も単純かつ粗雑であり、最も安全性が低くなります。これは、インターフェイスがすべてのドメイン名に対してクロスドメイン リクエストを行うことを許可することを意味します。ただし、一般的な実際のビジネスでは、インターフェイスでは、すべてではなく、1 つまたは少数の Web サイトに対してクロスドメイン リクエストのアクセス許可のみを許可することが予想されます。
それでは、賢明な方であれば、通常のルールを記述するだけで複数のドメイン名をホワイトリストに登録するのは簡単ではないかと考えているはずです。それが機能しない場合は、Access-Control-Allow-Origin 属性をカンマで区切られた複数のドメイン名に直接構成する方が良いのではないでしょうか?
このように:
res.header('Access-Control-Allow-Origin', '*.666.com'); // 或者如下 res.header('Access-Control-Allow-Origin', 'a.666.com,b.666.com,c.666.com');
申し訳ありませんが、この書き方は無効です。 Node.js では、応答ヘッダーの Access-Control-Allow-Origin 属性は (*) 以外の正規表現と一致できず、ドメイン名をカンマで区切ることもできません。つまり、Access-Control-Allow-Origin の属性値は、単一のドメイン名 文字列 または (*) にのみ設定できます。 複数のドメイン名を許可する必要があり、安全でない *
ワイルドカードを使用したくないため、複数のドメイン名ホワイトリストに対して CORS を構成できないというのは本当ですか?複数のドメイン名ホワイトリストを使用した CORS は実際に実現可能です。国を救うにはちょっとしたひねりが必要です。
マルチドメインホワイトリストのCORS実装原則
特定の原則については、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 === '*') {
// allow any origin
headers.push([{
key: 'Access-Control-Allow-Origin',
value: '*'
}]);
} else if (isString(options.origin)) {
// fixed origin
headers.push([{
key: 'Access-Control-Allow-Origin',
value: options.origin
}]);
headers.push([{
key: 'Vary',
value: 'Origin'
}]);
} else {
isAllowed = isOriginAllowed(requestOrigin, options.origin);
// reflect origin
headers.push([{
key: 'Access-Control-Allow-Origin',
value: isAllowed ? requestOrigin : false
}]);
headers.push([{
key: 'Vary',
value: 'Origin'
}]);
}
return headers;
}
function configureMethods(options) {
var methods = options.methods;
if (methods.join) {
methods = options.methods.join(','); // .methods is an array, so turn it into a string
}
return {
key: 'Access-Control-Allow-Methods',
value: methods
};
}
function configureCredentials(options) {
if (options.credentials === true) {
return {
key: 'Access-Control-Allow-Credentials',
value: 'true'
};
}
return null;
}
function configureAllowedHeaders(options, req) {
var allowedHeaders = options.allowedHeaders || options.headers;
var headers = [];
if (!allowedHeaders) {
allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers
headers.push([{
key: 'Vary',
value: 'Access-Control-Request-Headers'
}]);
} else if (allowedHeaders.join) {
allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string
}
if (allowedHeaders && allowedHeaders.length) {
headers.push([{
key: 'Access-Control-Allow-Headers',
value: allowedHeaders
}]);
}
return headers;
}
function configureExposedHeaders(options) {
var headers = options.exposedHeaders;
if (!headers) {
return null;
} else if (headers.join) {
headers = headers.join(','); // .headers is an array, so turn it into a string
}
if (headers && headers.length) {
return {
key: 'Access-Control-Expose-Headers',
value: headers
};
}
return null;
}
function configureMaxAge(options) {
var maxAge = options.maxAge && options.maxAge.toString();
if (maxAge && maxAge.length) {
return {
key: 'Access-Control-Max-Age',
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 === 'Vary' && 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 === 'OPTIONS') {
// 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 !== 'function') {
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 === 'function') {
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 === 'function') {
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;
}());
Access-Control-Allow-Origin 属性 複数のドメイン名を設定できないことが明らかになったので、このパスは諦める必要があります。
最も一般的で効果的な方法は、リクエストされたヘッダーの Origin 属性値 (req.header.origin) がドメイン名のホワイトリストに含まれているかどうかをサーバー側で判断することです。ホワイトリストに含まれている場合は、Access-Control-Allow-Origin を現在の Origin 値に設定します。これにより、Access-Control-Allow-Origin の単一ドメイン名の要件が満たされ、現在のリクエストがアクセスされることが保証されます。ホワイトリストにない場合は、
エラー メッセージが返されます。 このようにして、クロスドメインリクエストの検証をブラウザからサーバーに転送します。 Origin 文字列の検証は、通常の文字列の検証と同等になります。
配列具体的なコードは次のとおりです:まとめ 以上がCORS クロスドメイン リクエストでマルチドメイン名のホワイトリストを設定するための Node.js のサンプル コード共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。// 判断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 = [ // 域名白名单
'*.233.666.com',
'hello.world.com',
'hello..*.com'
];
app.post('a/b', 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('Access-Control-Allow-Credentials', 'true');
// 你的业务代码逻辑代码 ...
// ...
} else {
res.send({ code: -2, msg: '非法请求' });
}
});