【发布时间】:2015-03-14 04:30:45
【问题描述】:
TL;DR
如何拦截请求,为 ID ping 不同的路由,将 ID 存储在会话中,然后使用我刚刚获得的 ID 继续原始请求(尤其是带有有效负载的 PUT/POST)?
背景
我正在使用 HapiJS (8) 将来自客户端的请求代理到现有 API(我无法控制流程/逻辑)。 API 要求每个请求在查询字符串或有效负载中包含一个“会话 ID”(取决于 http 方法)。为了获得会话 ID,我所要做的就是要求一个……不需要用户名/密码(它在标题中使用基本身份验证)。如果不续订,会话 ID 每 24 小时过期一次。每个客户端都有自己的会话 ID。
我目前正在使用hapi-auth-cookie 来存储会话 ID 的值,当需要 ID 时会查询该值。如果 ID 已过期或为空,我需要请求一个新 ID,然后客户端的请求才能成功代理到 API。
当前解决方案
当客户端的请求方法是“GET”时,我正在使用hapi-auth-cookie docs 中描述的appendNext 配置非常优雅地处理这个挑战。请求被 hapi-auth-cookie 拦截,如果需要新的会话 ID,则向该特定路由发送请求以获取它,API 返回 ID,然后分配给 Hapi 会话,然后(使用 @ 987654323@) a reply.redirect 返回到它完成的原始 GET 请求。无缝而优雅。
但是,我无法弄清楚如何使用包含数据负载的不同 http 方法来完成相同的流程。
除了reply.redirect 之外,还有什么东西可以在保持原始有效负载和方法的同时实现相同的目标吗?还是有更好的方法来做到这一点?
代码(目前适用于“GET”请求的代码)
主应用文件(hapi-auth-cookie 配置)
# 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
【问题讨论】:
-
请填写邮政编码。这听起来像是您可以在
onPreHandler或onRequest(取决于您当时需要的信息)服务器扩展点中轻松处理的事情。此时,您可以在点击处理程序之前更改请求。查看请求生命周期:hapijs.com/api#request-lifecycle -
或者您可以在
mapUri选项中将reply.proxy与您的ID/端点逻辑一起使用。不过,一些代码将有助于决定这一点。 -
@MattHarrison 添加了当前适用于“GET”请求的代码示例。我知道请求生命周期(让我从 Express 切换的重要因素之一),但我不确定如何利用它来解决这个问题。谢谢!
标签: node.js session cookies proxy hapijs