【Kibana】如何优雅的提交数据并且避免系统的跨域检查

来源:互联网 发布:域名抵押贷款骗局 编辑:程序博客网 时间:2024/05/11 19:22

在开发Kibana 4.x的验证插件模块的时候,遇到一个问题是当使用POST的方式提交表单数据时,会引起Kibana服务器的跨域检查机制,从而收到如下错误:

{"statusCode":400,"error":"Bad Request","message":"Missing kbn-version header"}

当前开发环境下如下:

  • kibana 服务器版本:4.6.4
  • Kibana Plugin Yeoman Generator 用于生成Plugin插件代码结构
  • 依赖的npm模块,ldapjs(AD验证),Q(Promise模式)

kibana 服务器所需要掌握的开发技术:

  • hapijs -服务器中间件,需要了解Server的lifecycle概念
  • hapi-auth-cookie 基于Session sid的安全cookie验证
  • AngularJS前端开发技术

查阅了一下Kibana4.6的源代码 ibana/src/server/http/xsrf.js

 const disabled = config.get('server.xsrf.disableProtection');  // COMPAT: We continue to check on the kbn-version header for backwards  // compatibility since all existing consumers have been required to use it.  const versionHeader = 'kbn-version';  const xsrfHeader = 'kbn-xsrf';  server.ext('onPostAuth', function (req, reply) {    if (disabled) {      return reply.continue();    }    const isSafeMethod = req.method === 'get' || req.method === 'head';    const hasVersionHeader = versionHeader in req.headers;    const hasXsrfHeader = xsrfHeader in req.headers;    if (!isSafeMethod && !hasVersionHeader && !hasXsrfHeader) {      return reply(badRequest(`Request must contain an ${xsrfHeader} header`));    }    return reply.continue();  });

明显最简单的方式就是在配置文件中,添加server.xsrf.disableProtection节点,即可关闭相关检查。但显然这个方式也是比较简单粗暴的方法,不是很推荐。
其次我们观察到Kibana服务器主要会检查3个条件,是否是一个Get请求,Http请求头中是否包含 Version版本和xsrf的跨域信息,既然明确了改造方向,我们就来看下如何注入代码来实现功能。

1) 首先把客户端POST请求,在服务器端设置为GET,并且设置头部信息

server.ext('onRequest', function (req, reply) {            if(req.path=='/'||req.path=='/login'){                if(req.method == 'post'&& req.path == '/login'){                    //TO compatible kibana xsrf protection detected                    req.setMethod('get');                    req.headers['kbn-xsrf'] = 'kbn-version: 4.6.4';                    req.headers['kbn-version'] = '4.6.4';                    readPostData(req).then(function(){                       return reply.continue();                    });                }                else                {                    return reply.continue();                 }            }            else            {                 return reply.continue();            }        });

2) 由于在Hapi的生命周期内,原先POST的数据无法再通过Request.payload属性来访问,需要通过Node原始对象来取得客户端传入的参数

const readPostData = function(req){        var deferred = Q.defer();        var payloaddata = '';        //post data like: username=admin&password=admin        req.raw.req.on('data',function(chunk){          payloaddata = ''+chunk;          payloaddata.split('&').forEach(function(kv){              var toks = kv.split('=');              req.app[toks[0]] = decodeURI(toks[1]);              //console.log('set '+ toks[0] +': '+ req.app[toks[0]]);          });         deferred.resolve();        });         return deferred.promise;     }

至此再也不需要担心登录提交的信息被显示在URL里面了 :)

0 0