如何拦截请求,ping不同的路由获取ID,将ID存储在会话中,然后使用我刚刚获得的ID继续原始请求(特别是带有效负载的PUT / POST)?
我正在使用HapiJS(8)将客户端的请求代理到现有的API(我无法控制进程/逻辑)。 API要求每个请求都包含一个'会话ID'在查询字符串或有效负载中(取决于http方法)。为了获得会话ID,我所要做的只是要求一个...没有用户名/密码需要(它在标题中使用Basic auth)。会话ID如果没有续订,则每24小时到期一次。每个客户都有自己的会话ID。
我目前正在使用hapi-auth-cookie来存储会话ID的值,当需要ID时会查询该值。如果ID已过期或为空,我需要在客户的请求成功代理API之前请求新的。
当客户的请求方法是“GET'”时,我正在使用hapi-auth-cookie docs中描述的appendNext
配置非常优雅地处理此挑战。该请求被hapi-auth-cookie截获,如果需要新的会话ID,则向该特定路由发送请求以获取它,API返回ID,然后将其分配给Hapi会话,然后(使用{ {3}})reply.redirect
返回完成的原始GET请求。无缝而优雅。
但是,我无法弄清楚如何使用包含数据有效负载的不同http方法来完成相同的流程。
除了reply.redirect
之外还有什么能够在保持原有的有效载荷和方法的同时实现相同的目标吗?或者有更好的方法来做到这一点吗?
主应用程序文件(hapi-auth-cookie configs)
# register plugins
server.register require('./plugins')
, (err) ->
throw err if err
# set authentication (NLS) configs
server.auth.strategy 'session', 'cookie',
password: 'session_pwd'
cookie: 'ghsid'
redirectTo: '/api/SessionID' #get new session ID
isSecure: config.get 'ssl'
appendNext: true
ttl: config.get 'session_length'
利用会话身份验证并调用hapi-auth-cookie插件的控制器:
simpleRequest:
auth: 'session'
handler: (request, reply) ->
qs = Qs.stringify request.query
request.papi_url = "/api/route/sample?#{qs}"
reply.proxy
mapUri: (request, reply) ->
auth = config.get 'basic_auth'
api_host = config.get 'api_host'
papi_url = request.papi_url
path = api_host + papi_url
next null, path, {authorization: auth}
获取新会话ID的路线
module.exports = [
{
path: '/api/SessionID'
method: 'GET'
config: SessionController.session
}
]
会话控制器
Wreck = require 'wreck'
config = require 'config'
module.exports =
session:
description: 'Get new session ID'
auth:
mode: 'try'
strategy: 'session'
plugins:
'hapi-auth-cookie':
redirectTo: false
handler: (request, reply) ->
# request configs
papi_url = "/Session"
api_host = config.get 'api_host'
url = api_host + papi_url
opts =
headers:
'Authorization': config.get 'basic_auth'
'content-type': 'application/json;charset=UTF-8'
# make request to PAPI
Wreck.post url, opts, (err, res, body) ->
throw new Error err if err
try
bdy = JSON.parse body
sess =
nls: bdy.SessionId
if bdy.SessionId
# authenticate user with NLS
request.auth.session.set sess
# redirect to initial route
reply.redirect request.url.query.next
else
return throw new Error
catch err
throw new Error err
根据Matt Harrison的回答,我创建了一个自定义插件,它被注册为身份验证方案,因此我可以按路径控制它。
这是插件代码:
Wreck = require 'wreck'
config = require 'config'
exports.register = (server, options, next) ->
server.auth.scheme 'cookie', internals.implementation
next()
exports.register.attributes =
name: 'Hapi Session Interceptor'
version: '1.0.0'
internals = {}
internals.implementation = (server, options, next) ->
scheme = authenticate: (request, reply) ->
validate = ->
session = request.state.sessionID
unless session
return unauthenticated()
reply.continue(credentials: {'session': session})
unauthenticated = ->
api_url = "/SessionID"
api_host = config.get 'api_host'
url = api_host + api_url
opts =
headers:
'Authorization': config.get 'basic_auth'
'content-type': 'application/json;charset=UTF-8'
# make request to API
Wreck.post url, opts, (err, res, body) ->
throw new Error err if err
bdy = JSON.parse body
sess =
session: bdy.SessionId
if bdy.SessionId
reply.state 'sessionID', bdy.SessionId
reply.continue(credentials: sess)
else
return throw new Error
validate()
return scheme
答案 0 :(得分:1)
虽然不完全忠实于您的代码,但我已经汇总了一个包含我认为您正在使用的所有部分的示例。
我已经制作了一个service
插件来代表您的 API。 upstream
插件代表您要代理的实际上游API。
所有请求都通过service
并被代理到upstream
,它只打印出收到的所有标头和有效负载。
如果原始请求不包含带有sessionId的cookie,则会在upstream
上点击一条路线以获取一条路线。然后,当响应回到下游时,将使用此值设置cookie。
代码在这里:https://github.com/mtharrison/hapijs-proxy-trouble
尝试使用curl和浏览器。
获取: curl http://localhost:4000
POST W / PAYLOAD: curl -X POST -H "content-type: application/json" -d '{"example":"payload"}' http://localhost:4000
<强> index.js 强>
var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection({ port: 4000, labels: ['service'] }); // Your service
server.connection({ port: 5000, labels: ['upstream']}); // Pretend upstream API
server.state('session', {
ttl: 24 * 60 * 60 * 1000,
isSecure: false,
path: '/',
encoding: 'base64json'
});
server.register([{
register: require('./service')
}, {
register: require('./upstream')
}],
function (err) {
if (err) {
throw err;
}
server.start(function () {
console.log('Started!');
});
});
<强> service.js 强>
var Wreck = require('wreck');
exports.register = function (server, options, next) {
// This is where the magic happens!
server.select('service').ext('onPreHandler', function (request, reply) {
var sessionId = request.state.session;
var _done = function () {
// Set the cookie and proceed to the route
request.headers['X-Session-Id'] = sessionId;
reply.state('session', sessionId);
reply.continue();
}
if (typeof sessionId !== 'undefined')
return _done();
// We don't have a sessionId, let's get one
Wreck.get('http://localhost:5000/sessionId', {json: true}, function (err, res, payload) {
if(err) {
throw err;
}
sessionId = payload.id;
_done();
});
});
server.select('service').route({
method: '*',
path: '/{p*}', // Proxies all routes and methods
handler: {
proxy: {
host: 'localhost',
port: 5000,
protocol: 'http',
passThrough: true
}
}
});
next();
};
exports.register.attributes = {
name: 'your-service'
};
<强> upstream.js 强>
exports.register = function (server, options, next) {
server.select('upstream').route([{
method: '*',
path: '/{p*}',
handler: function (request, reply) {
// Just prints out what it received for headers and payload
// To prove we got send the original payload and the sessionID header
reply({
originalHeaders: request.headers,
originalPayload: request.payload,
})
}
}, {
method: 'GET',
path: '/sessionId',
handler: function (request, reply) {
// Returns a random session id
reply({ id: (Math.floor(Math.random() * 1000)) });
}
}]);
next();
};
exports.register.attributes = {
name: 'upstream'
};