您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Node.js中怎么設置CORS跨域請求,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
CORS
說到CORS,相信前端兒都不陌生,這里我就不多說了,具體可以看看這篇文章。
CORS,主要就是配置Response響應頭中的 Access-Control-Allow-Origin 屬性為你允許該接口訪問的域名。最常見的設置是:
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Credentials', 'true'); // 允許服務器端發送Cookie數據
然而,這樣的設置是最簡單粗暴,同時也是最不安全的。它表示該接口允許所有的域名對它進行跨域請求。然而,在一般實際業務中,都希望該接口只允許對某一個或幾個網站開放跨域請求權限,而非全部。
那么,聰明的你肯定想著,多域名白名單還不簡單嗎,寫個正則就好啦?再不行,直接配置 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的響應頭Header中的 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 屬性已經明確不能設置多個域名,那么我們只得放棄這條路了。
最流行也是最有效的方法就是,在服務器端判斷請求的Header中Origin屬性值(req.header.origin)是否在我們的域名白名單列表內。如果在白名單列表內,那么我們就把 Access-Control-Allow-Origin 設置成當前的Origin值,這樣就滿足了Access-Control-Allow-Origin 的單一域名要求,也能確保當前請求通過訪問;如果不在白名單列表內,則返回錯誤信息。
這樣,我們就把跨域請求的驗證,從瀏覽器端轉移到服務端來了。對Origin字符串的驗證就變成了相當于常規字符串的驗證,我們不僅可以使用數組列表驗證,還可以使用正則匹配。
具體代碼如下:
// 判斷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: '非法請求' }); } });
關于Node.js中怎么設置CORS跨域請求就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。