欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Node.js設(shè)置CORS跨域請求中多域名白名單的方法

 更新時(shí)間:2017年03月28日 11:23:43   作者:子匠_Zijor  
這篇文章主要介紹了Node.js設(shè)置CORS跨域請求中多域名白名單的方法,文中通過示例代碼介紹的非常詳細(xì),相信對大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。

CORS

說到CORS,相信前端兒都不陌生,這里我就不多說了,具體可以看看這篇文章。

CORS,主要就是配置Response響應(yīng)頭中的 Access-Control-Allow-Origin 屬性為你允許該接口訪問的域名。最常見的設(shè)置是:

res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true'); // 允許服務(wù)器端發(fā)送Cookie數(shù)據(jù)

然而,這樣的設(shè)置是最簡單粗暴,同時(shí)也是最不安全的。它表示該接口允許所有的域名對它進(jìn)行跨域請求。然而,在一般實(shí)際業(yè)務(wù)中,都希望該接口只允許對某一個或幾個網(wǎng)站開放跨域請求權(quán)限,而非全部。

那么,聰明的你肯定想著,多域名白名單還不簡單嗎,寫個正則就好啦?再不行,直接配置 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中,res的響應(yīng)頭Header中的 Access-Control-Allow-Origin 屬性不能匹配除 (*) 以外的正則表達(dá)式的,域名之間不能也用逗號分隔。也就是說, Access-Control-Allow-Origin 的屬性值只允許設(shè)置為單個確定域名字符串或者 (*)。

既然我們希望允許的是多個域名,也不愿意使用不安全的 * 通配符,難道就真不能配置多域名白名單的CORS了嗎?

多域名白名單的CORS確實(shí)是可以實(shí)現(xiàn)的。只是有一點(diǎn)曲線救國的味道。

多域名白名單的CORS實(shí)現(xiàn)原理

具體原理可以參考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;

}());

實(shí)現(xiàn)原理是這樣的:

既然 Access-Control-Allow-Origin 屬性已經(jīng)明確不能設(shè)置多個域名,那么我們只得放棄這條路了。

最流行也是最有效的方法就是,在服務(wù)器端判斷請求的Header中Origin屬性值(req.header.origin)是否在我們的域名白名單列表內(nèi)。如果在白名單列表內(nèi),那么我們就把 Access-Control-Allow-Origin 設(shè)置成當(dāng)前的Origin值,這樣就滿足了Access-Control-Allow-Origin 的單一域名要求,也能確保當(dāng)前請求通過訪問;如果不在白名單列表內(nèi),則返回錯誤信息。

這樣,我們就把跨域請求的驗(yàn)證,從瀏覽器端轉(zhuǎn)移到服務(wù)端來了。對Origin字符串的驗(yàn)證就變成了相當(dāng)于常規(guī)字符串的驗(yàn)證,我們不僅可以使用數(shù)組列表驗(yàn)證,還可以使用正則匹配。

具體代碼如下:

// 判斷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響應(yīng)頭的origin屬性

 // 判斷請求是否在域名白名單內(nèi)
 if(isOriginAllowed(reqOrigin, ALLOW_ORIGIN)) {
 // 設(shè)置CORS為請求的Origin值
 res.header("Access-Control-Allow-Origin", reqOrigin);
 res.header('Access-Control-Allow-Credentials', 'true');

 // 你的業(yè)務(wù)代碼邏輯代碼 ...
 // ...
 } else {
 res.send({ code: -2, msg: '非法請求' });
 }
});

Oh yeah,簡直完美~

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • 在linux中使用包管理器安裝node.js

    在linux中使用包管理器安裝node.js

    這篇文章主要介紹了在linux中使用包管理器安裝node.js的方法以及具體安裝過程,非常詳細(xì),推薦給大家,有需要的小伙伴參考下吧。
    2015-03-03
  • 深入淺析Node.js單線程模型

    深入淺析Node.js單線程模型

    Node.js采用 事件驅(qū)動 和 異步I/O 的方式,實(shí)現(xiàn)了一個單線程、高并發(fā)的運(yùn)行時(shí)環(huán)境,而單線程就意味著同一時(shí)間只能做一件事,那么Node.js如何利用單線程來實(shí)現(xiàn)高并發(fā)和異步I/O?本文將圍繞這個問題來探討Node.js的單線程模型
    2017-07-07
  • kafka調(diào)試中遇到Connection to node -1 could not be established. Broker may not be available.

    kafka調(diào)試中遇到Connection to node -1 could not be established. Br

    這篇文章主要介紹了kafka調(diào)試中遇到Connection to node -1 could not be established. Broker may not be available的解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-09-09
  • 如何使用docker直接運(yùn)行不同版本nodejs命令

    如何使用docker直接運(yùn)行不同版本nodejs命令

    這篇文章主要介紹了如何使用docker直接運(yùn)行不同版本nodejs命令,在不支持高版本Node.js的舊版操作系統(tǒng)上,可以通過Docker容器技術(shù)解決兼容性問題,需要的朋友可以參考下
    2024-10-10
  • 使用Koa實(shí)現(xiàn)一個獲取視頻播放地址的接口

    使用Koa實(shí)現(xiàn)一個獲取視頻播放地址的接口

    在本節(jié)課中,我們將學(xué)習(xí)如何使用 Koa 實(shí)現(xiàn)一個獲取視頻播放地址的接口,我們將創(chuàng)建一個控制器,通過視頻 ID 獲取播放地址,并設(shè)置相應(yīng)的路由,最后,我們將使用 Postman 進(jìn)行測試,感興趣的朋友可以參考下
    2024-05-05
  • Node.js和MongoDB實(shí)現(xiàn)簡單日志分析系統(tǒng)

    Node.js和MongoDB實(shí)現(xiàn)簡單日志分析系統(tǒng)

    這篇文章主要介紹了Node.js和MongoDB實(shí)現(xiàn)簡單日志分析系統(tǒng),本文給出了服務(wù)器端、客戶端、圖表生成、Shell自動執(zhí)行等功能的實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-04-04
  • nodejs文件夾深層復(fù)制功能

    nodejs文件夾深層復(fù)制功能

    這篇文章主要介紹了nodejs文件夾深層復(fù)制功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Node.js中開發(fā)樹形結(jié)構(gòu)接口的實(shí)現(xiàn)

    Node.js中開發(fā)樹形結(jié)構(gòu)接口的實(shí)現(xiàn)

    本文介紹了Node.js中開發(fā)樹形結(jié)構(gòu)接口的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • nodeJs事件循環(huán)運(yùn)行代碼解析

    nodeJs事件循環(huán)運(yùn)行代碼解析

    這篇文章主要為大家介紹了nodeJs事件循環(huán)運(yùn)行代碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • nodeJs編寫錯誤處理中間件問題

    nodeJs編寫錯誤處理中間件問題

    這篇文章主要介紹了nodeJs編寫錯誤處理中間件問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12

最新評論