摘要:最近正在負(fù)責(zé)一個(gè)新平臺(tái)的構(gòu)建和開發(fā),有一個(gè)場(chǎng)景需要對(duì)應(yīng)用做新增,修改和撤回的操作起先是因?yàn)橹皩戇^(guò)類型的功能,不想在和以前一樣一個(gè)操作類型一個(gè),覺(jué)得代碼太過(guò)冗余了。
最近正在負(fù)責(zé)一個(gè)新平臺(tái)的構(gòu)建和開發(fā),有一個(gè)場(chǎng)景需要對(duì)應(yīng)用做新增,修改和撤回的操作
起先是因?yàn)橹皩戇^(guò)類型的功能,不想在和以前一樣一個(gè)操作類型一個(gè)api,覺(jué)得代碼太過(guò)冗余了。
于是有了以下的構(gòu)思
第一版
將當(dāng)前界面所有api請(qǐng)求,合并成一個(gè)request,以type作為操作類型的區(qū)分,data為提交的數(shù)據(jù)
這樣當(dāng)前界面所有操作都使用一個(gè)接口來(lái)處理,并且問(wèn)題統(tǒng)一處理
處理token失效
處理catch
處理通信成功后都通知
處理權(quán)限
優(yōu)化版
當(dāng)設(shè)計(jì)成第一版后,我覺(jué)得操作類型暴露在外面有些不妥,起先想的是后端生成隨機(jī)碼和對(duì)應(yīng)的加密值,通過(guò)解密拿到方法名。
后來(lái)優(yōu)化了一下,加入了url來(lái)源的判斷,還能防止postman的攻擊
后端代碼如下:
redisImp為redis utils為工具類 token和權(quán)限的檢查放在了外層,進(jìn)入方法的都當(dāng)成token和權(quán)限通過(guò)的 const apiPrefix = "ApiType:"; // 通過(guò)viewConfig生成對(duì)應(yīng)配置 async function generateConfig (owner, viewConfig) { var viewName = viewConfig.name; // 界面名稱 var viewMethods = viewConfig.methods; // 界面所支持的操作方法 let key = apiPrefix + owner + ":" + viewName; await redisImp.del(key); let para = [], config = [], secret = []; // 生成10個(gè)長(zhǎng)度為12的隨機(jī)碼 for (var i = 0; i < 10; i++) { var randomKey = utils.generateRandomStr(12); config.push(randomKey); } // 生成三個(gè)10一下的數(shù)字 var random1 = Math.ceil(Math.random() * 10); var random2 = Math.ceil(Math.random() * 10); var random3 = Math.ceil(Math.random() * 10); // todo 檢查3個(gè)隨機(jī)數(shù)是否相等 var randomList = [random1, random2, random3]; // 生成隨機(jī)碼和操作方法的關(guān)聯(lián)數(shù)據(jù) viewMethods.forEach(function (value, index) { para.push(config[randomList[index]]); para.push(value); secret.push(randomList[index]); }) // aes加密 var enc = utils.cryptedAES(secret.toString()); let redisResult = await redisImp.hSet(key, para); if (redisResult.code === 200) { return { apis: config, secret: enc } } return null; } // 獲取界面的配置 function getViewConfig (ctx) { var referer = ctx.request.header.referer; // 原始url var origin = ctx.request.header.origin; // 來(lái)源 var config; if (!referer || !origin) { // todo 處理異常訪問(wèn) return config; } else { var fontUrl = referer.replace(origin, "").split("?"); // 去除domain和url參數(shù)后的路徑 switch (fontUrl[0]) { case "/app/base": { config = { name: "appBase", // 界面名稱 methods: ["add", "modify", "retract"] // 界面操作權(quán)限 } break; } default: { // todo 處理異常攻擊 } } } return config; } // 獲取配置,暴露給前端的api接口 const getConfig = async (ctx) => { const fName = _name + "getConfig"; lifecycleLog.info("[Enter] " + fName); // 獲取當(dāng)前用戶id const redisResult = await redis.GetTokenValue(ctx, "id"); let owner; if (redisResult.code === 200) { owner = redisResult.data; } else { ctx.body = redisResult; return; } // 獲取界面配置 var viewConfig = getViewConfig(ctx); if (viewConfig) { var result = await generateConfig(owner, viewConfig); if (result) { // 生成成功后返回給前端 ctx.body = Object.assign({code: 200}, result); } else { ctx.body = controller.dataError(); } } else { ctx.body = controller.dataError(); } lifecycleLog.info("[Return] " + fName); } const appBase = require("./appBase") // 處理應(yīng)用界面的接口 const handleAppBaseData = async (ctx) => { const fName = _name + "handleAppBaseData"; lifecycleLog.info("[Enter] " + fName); var viewConfig = getViewConfig(ctx); if (viewConfig) { const name = ctx.request.body.name; // 前端傳過(guò)來(lái)的操作碼 const para = ctx.request.body.data; // 前端傳過(guò)來(lái)的數(shù)據(jù) let data; try { data = JSON.stringify(para); } catch (err) { ctx.body = controller.dataError(); return; } // 驗(yàn)證數(shù)據(jù)完整性 if (controller.dataMissed(ctx, fName, ctx.request.body, name + data)) { return; } const redisResult = await redis.GetTokenValue(ctx, "id"); let owner; if (redisResult.code === 200) { owner = redisResult.data; } else { ctx.body = redisResult; return; } // 從redis拿到當(dāng)前用戶在當(dāng)前界面的操做類型 let apiType = await redisImp.hGet(apiPrefix + owner + ":" + viewConfig.name, name); if (apiType.code === 200) { if (apiType.data.length) { var methods = apiType.data[0]; // 添加操作 if (methods === "add") { await appBase.add(ctx, para, owner); } else { let option = { _id: para._id, owner: owner }; // 檢測(cè)該用戶是否擁有該app const gameResult = await commonModel.getInfo(ctx, collection, option); if (gameResult) { if (gameResult.code === 200) { var gameDoc = gameResult.info["_doc"]; } else { ctx.body = controller.dataError(); return; } } else { ctx.body = controller.serverError(); return; } // 修改操作 if (methods === "modify") { await appBase.modify(ctx, para, gameDoc); } else if (methods === "retract") { // 撤回炒作 await appBase.retract(ctx, gameDoc); } else { ctx.body = controller.dataError(); return; } } // 如果入庫(kù)成功,則將新一輪的操作碼反給前端 if (ctx.body.code === 200) { var result = await generateConfig(owner, viewConfig); ctx.body = Object.assign(ctx.body, result); } } else { ctx.body = controller.dataError(); } } else { ctx.body = controller.serverError(); } } else { ctx.body = controller.dataError(); } lifecycleLog.info("[Return] " + fName); }
這是返回的結(jié)構(gòu)
前端就不上代碼了,稍微說(shuō)下應(yīng)該都能明白 1. 進(jìn)入界面的時(shí)候,請(qǐng)求getConfig 2. 前端拿到數(shù)據(jù)進(jìn)行解密 3. 操作界面的時(shí)候,發(fā)送操作碼和數(shù)據(jù) 4. 請(qǐng)求完成,拿到新的操作碼進(jìn)行本地更新,并對(duì)之前的操作作出反應(yīng)(數(shù)據(jù)更新/界面跳轉(zhuǎn)/彈框提示等)
延伸版
獲取界面配置,可以放在一個(gè)任何界面都會(huì)訪問(wèn)的地方,統(tǒng)一處理,后端配好路由的url即可
解決/預(yù)防了哪些問(wèn)題
1.代碼冗余問(wèn)題
2.爬蟲問(wèn)題(由于所有的操作入?yún)⒍际莿?dòng)態(tài)返回且隨機(jī)生成,爬蟲們沒(méi)法按著一個(gè)接口和數(shù)據(jù)爬取數(shù)據(jù),增大了難度)
3.非正常的訪問(wèn)
以上就是我對(duì)API安全策略的想法,如有異議或新的方式歡迎評(píng)論留言。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/102750.html
摘要:微服務(wù)能夠?yàn)閼?yīng)用程序設(shè)計(jì)提供一種更具針對(duì)性范圍性與模塊性的實(shí)現(xiàn)方案。安全微服務(wù)部署模式可謂多種多樣但其中使用最為廣泛的當(dāng)數(shù)每主機(jī)服務(wù)模式。在微服務(wù)環(huán)境下,安全性往往成為最大的挑戰(zhàn)。不同微服務(wù)之間可通過(guò)多種方式建立受信關(guān)系。 每個(gè)人都在討論微服務(wù),每個(gè)人也都希望能夠?qū)崿F(xiàn)微服務(wù)架構(gòu),而微服務(wù)安全也日漸成為大家關(guān)注的重要問(wèn)題。今天小數(shù)與大家分享的文章,就從應(yīng)用層面深入探討了應(yīng)對(duì)微服務(wù)安全挑戰(zhàn)...
摘要:幾年前,行業(yè)預(yù)測(cè)分析人員表示,一旦企業(yè)決定了他們的云計(jì)算戰(zhàn)略,他們將會(huì)首先構(gòu)建私有云,并在以后根據(jù)需要添加公共云服務(wù)。如果要在本地實(shí)施容器或作為云計(jì)算部署的一部分實(shí)施容器,則需要確保其工作負(fù)載是安全的。幾年前,行業(yè)預(yù)測(cè)分析人員表示,一旦企業(yè)決定了他們的云計(jì)算IT戰(zhàn)略,他們將會(huì)首先構(gòu)建私有云,并在以后根據(jù)需要添加公共云服務(wù)。但這種事情并沒(méi)有發(fā)生。事實(shí)證明,采用云計(jì)算可以盡快讓組織的董事會(huì)分配資...
摘要:綜上所述,認(rèn)為沒(méi)有提供的保護(hù),用戶會(huì)過(guò)得更好安全研究人員并不完全反對(duì)這一決定。內(nèi)容安全策略是一個(gè)額外的安全層,用于檢測(cè)并削弱某些特定類型的攻擊,包括跨站腳本和數(shù)據(jù)注入攻擊等。 這是關(guān)于web安全性系列文章的第 三 篇,其它的可點(diǎn)擊以下查看: Web 應(yīng)用安全性: 瀏覽器是如何工作的 Web 應(yīng)用安全性: HTTP簡(jiǎn)介 目前,瀏覽器已經(jīng)實(shí)現(xiàn)了大量與安全相關(guān)的頭文件,使攻擊者更難利用漏...
摘要:文件完整性監(jiān)測(cè)持續(xù)監(jiān)控您的云服務(wù)器,保護(hù)重要的系統(tǒng)二進(jìn)制文件和配置文件不會(huì)受到未經(jīng)授權(quán)的或惡意的變更。首先會(huì)記錄下云服務(wù)器系統(tǒng)的清潔狀態(tài),作為基準(zhǔn)。您可以通過(guò)一個(gè)在線管理控制臺(tái),監(jiān)控所有的云服務(wù)器。 DEVSECOPS 所面臨的挑戰(zhàn) 敏捷開發(fā)和 DevOps 方法的出現(xiàn)使軟件開發(fā)的速度與質(zhì)量都有所提升,但它們不經(jīng)意地也為安全機(jī)構(gòu)增壓不少。從前的安全策略是基于靜態(tài)數(shù)據(jù)的,而在產(chǎn)品上線前才...
閱讀 3917·2021-11-24 09:38
閱讀 3088·2021-11-17 09:33
閱讀 3863·2021-11-10 11:48
閱讀 1234·2021-10-14 09:48
閱讀 3123·2019-08-30 13:14
閱讀 2543·2019-08-29 18:37
閱讀 3386·2019-08-29 12:38
閱讀 1410·2019-08-29 12:30