热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【web平安】API的平安策略

近来正在担任一个新平台的构建和开辟,有一个场景须要对运用做新增,修正和撤回的操纵起先是因为之前写过范例的功用,不想在和之前一样一个操纵范例一个api,以为代码太甚冗余了。因而有了以

近来正在担任一个新平台的构建和开辟,有一个场景须要对运用做新增,修正和撤回的操纵

起先是因为之前写过范例的功用,不想在和之前一样一个操纵范例一个api,以为代码太甚冗余了。

因而有了以下的构想

  • 初版
    将当前界面一切api要求,合并成一个request,以type作为操纵范例的辨别,data为提交的数据
    《【web平安】API的平安策略》
    如许当前界面一切操纵都运用一个接口来处置惩罚,而且题目一致处置惩罚

    1. 处置惩罚token失效
    2. 处置惩罚catch
    3. 处置惩罚通讯胜利后都关照
    4. 处置惩罚权限
  • 优化版
    当设想成初版后,我以为操纵范例暴露在表面有些不妥,起先想的是后端天生随机码和对应的加密值,经由过程解密拿到要领名。
    厥后优化了一下,加入了url泉源的推断,还能防备postman的进击
    后端代码以下:

    redisImp为redis
    utils为东西类
    token和权限的搜检放在了外层,进入要领的都当做token和权限经由过程的
    const apiPrefix = 'ApiType:';
    // 经由过程viewConfig天生对应设置
    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个长度为12的随机码
    for (var i = 0; i <10; i++) {
    var randomKey = utils.generateRandomStr(12);
    config.push(randomKey);
    }
    // 天生三个10一下的数字
    var random1 = Math.ceil(Math.random() * 10);
    var random2 = Math.ceil(Math.random() * 10);
    var random3 = Math.ceil(Math.random() * 10);
    // todo 搜检3个随机数是不是相称
    var randomList = [random1, random2, random3];
    // 天生随机码和操纵要领的关联数据
    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; // 泉源
    var config;
    if (!referer || !origin) {
    // todo 处置惩罚非常接见
    return config;
    } else {
    var fOntUrl= referer.replace(origin, '').split('?'); // 去除domain和url参数后的途径
    switch (fontUrl[0]) {
    case '/app/base': {
    cOnfig= {
    name: 'appBase', // 界面称号
    methods: ['add', 'modify', 'retract'] // 界面操纵权限
    }
    break;
    }
    default: {
    // todo 处置惩罚非常进击
    }
    }
    }
    return config;
    }
    // 猎取设置,暴露给前端的api接口
    const getCOnfig= async (ctx) => {
    const fName = _name + 'getConfig';
    lifecycleLog.info('[Enter] ' + fName);
    // 猎取当前用户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')
    // 处置惩罚运用界面的接口
    const handleAppBaseData = async (ctx) => {
    const fName = _name + 'handleAppBaseData';
    lifecycleLog.info('[Enter] ' + fName);
    var viewCOnfig= getViewConfig(ctx);
    if (viewConfig) {
    const name = ctx.request.body.name; // 前端传过来的操纵码
    const para = ctx.request.body.data; // 前端传过来的数据
    let data;
    try {
    data = JSON.stringify(para);
    } catch (err) {
    ctx.body = controller.dataError();
    return;
    }
    // 考证数据完整性
    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拿到当前用户在当前界面的操做范例
    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
    };
    // 检测该用户是不是具有该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;
    }
    }
    // 假如入库胜利,则将新一轮的操纵码反给前端
    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);
    }

    • 这是返回的构造
      《【web平安】API的平安策略》

前端就不上代码了,轻微说下应当都能邃晓
1. 进入界面的时刻,要求getConfig
2. 前端拿到数据举行解密
3. 操纵界面的时刻,发送操纵码和数据
4. 要求完成,拿到新的操纵码举行当地更新,并对之前的操纵作出反应(数据更新/界面跳转/弹框提醒等)

  • 延长版
    猎取界面设置,能够放在一个任何界面都邑接见的处所,一致处置惩罚,后端配好路由的url即可
  • 处理/预防了哪些题目
    1.代码冗余题目
    2.爬虫题目(因为一切的操纵入参都是动态返回且随机天生,爬虫们没法按着一个接口和数据爬取数据,增大了难度)
    3.非正常的接见

以上就是我对API安全策略的主意,若有贰言或新的体式格局迎接批评留言。


推荐阅读
  • 006_Redis的List数据类型
    1.List类型是一个链表结构的集合,主要功能有push,pop,获取元素等。List类型是一个双端链表的结构,我们可以通过相关操作进行集合的头部或者尾部添加删除元素,List的设 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 在编写业务代码时,常常会遇到复杂的业务逻辑导致代码冗长混乱的情况。为了解决这个问题,可以利用中间件模式来简化代码逻辑。中间件模式可以帮助我们更好地设计架构和代码,提高代码质量。本文介绍了中间件模式的基本概念和用法。 ... [详细]
  • 本文详细介绍了git常用命令及其操作方法,包括查看、添加、提交、删除、找回等操作,以及如何重置修改文件、抛弃工作区修改、将工作文件提交到本地暂存区、从版本库中删除文件等。同时还介绍了如何从暂存区恢复到工作文件、恢复最近一次提交过的状态,以及如何合并多个操作等。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
author-avatar
没有1200
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有