Erlang 20

11.Orber拦截器 | 11. Orber Interceptors

11 Orber拦截器

11.1使用拦截器

对于Inter-ORB通信,例如通过IIOP,可以拦截请求和回复。为了能够使用InterceptorsOrber,interceptors必须定义配置参数。

配置Orber使用拦截器

interceptors必须定义配置参数,例如,作为命令行选项:

erl -orber interceptors "{native, ['myInterceptor']}"

有可能使用多个拦截器; 只需将它们添加到列表中,它们将按照它们出现在列表中的顺序调用。

也可以在运行时激活和停用拦截器,但这只会影响当前现有的连接。有关更多信息,请参阅Orber的参考手册,了解有关操作orber:activate_audit_trail/0/1orber:activate_audit_trail/0/1.

创建拦截器

每个提供的拦截器都必须输出以下功能:

  • new_out_connection / 3/5 - 当客户端应用程序调用驻留在远程ORB上的对象时,将调用其中一个操作。如果一个拦截器导出了两个版本,arity 3和5,那么将调用哪个操作是Orber内部的。

  • new_in_connection / 3/5 - 当客户端ORB尝试建立与目标ORB的连接时,将调用其中一个操作。如果一个拦截器导出了两个版本,arity 3和5,那么将调用哪个操作是Orber内部的。

  • out_request / 6 - 提供客户端ORB上的所有请求数据。

  • out_request_encoded / 6 - 类似于out_request请求主体是编码。

  • in_request_encoded / 6 - 在新的请求到达目标ORB后,请求数据以编码格式传递给拦截器。

  • in_request / 6 - 在调用目标对象的操作之前,调用拦截器in_request

  • out_reply / 6 - 在目标对象回复之后,out_reply操作被调用的结果是对象调用。

  • out_reply_encoded / 6 - 在将响应发送回客户端ORB之前,将以编码格式调用结果调用此操作。

  • in_reply_encoded / 6 - 在客户端ORB收到回复后,以编码格式回复此函数。

  • in_reply / 6 - 在将响应传递给客户端之前,调用该操作。

  • closed_in_connection / 1 - 当连接在客户端终止时,调用该函数。

  • closed_out_connection / 1 - 如果传出连接终止,将调用此操作。

操作new_out_connectionnew_in_connectionclosed_in_connectionclosed_out_connection操作仅调用一次,每个连接。对于每个远程CORBA对象的请求/响应,剩下的操作被调用,如下所示。

图11.1:Interceptor函数的调用顺序。

11.2 拦截器示例

假设我们希望创建一个简单的访问服务,其目的是:

  • 只允许ORB驻留在特定节点上的传入请求。

  • 限制任何客户端可以调用操作的对象。

  • 只允许传出请求调用有限的外部ORB%27。

  • 向每个二进制请求/回复正文添加校验和。

为了限制访问,我们使用一个保护所有信息的受保护和命名的ets表。 如何启动和维护ets-table是特定于实现的,但它包含{Node,ObjectTable,ChecksumModule},其中Node用作ets-key,ObjectTable是另一个ets-table的引用,我们在其中存储客户端是哪些对象 允许调用操作,并且ChecksumModule确定我们应该使用哪个模块来处理校验和。

new_in_connection(Arg, Host, Port) -> %% Since we only use one interceptor we do not care about the %% input Arg since it is set do undefined by Orber. case ets:lookup(in_access_table, Host) of [] -> %% We may want to log the Host/Port to see if someone tried %% to hack in to our system. exit("Access not granted" [{Host, ObjTable, ChecksumModule}] -> {ObjTable, ChecksumModule} end.

每当调用其中一个拦截器函数时,返回的元组(即{ObjTable,ChecksumModule})将作为第一个参数传递。除非连接尝试没有失败,否则我们现在准备好接收来自客户端ORB的请求。

当一个新的请求进入时,第一个被调用的拦截器函数是in_request_encoded。 我们将通过以下方式从编码请求主体中移除校验和:

in_request_encoded{ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) -> NewBin = ChecksumModule:remove_checksum(Bin), {NewBin, Extra}.

如果校验和检查失败,ChecksumModule应该调用exit / 1。 但是,如果检查成功,我们现在准备检查是否允许客户端ORB对象调用目标对象上的操作。 请注意,可以在in_request_encoded中运行这两项检查。 请注意,校验和计算必须相对较快以确保良好的吞吐量。

如果我们愿意,可以限制任何客户端只使用服务器导出的操作的子集:

in_request{ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Params, Extra) -> case ets:lookup(ObjTable, {ObjKey, Op}) of [] -> exit("Client tried to invoke illegal operation" [SomeData] -> {Params, Extra} end.

此时,Orber现在准备调用目标对象的操作。由于我们不关心答复是out_reply,函数什么也不做,即:

out_reply(_, _, _, _, Reply, Extra) -> {Reply, Extra}.

如果客户端ORB希望将校验和添加到回复中,我们使用以下命令添加它:

out_reply_encoded{ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) -> NewBin = ChecksumModule:add_checksum(Bin), {NewBin, Extra}.

警告

如果我们像上面的行为那样操作二进制文件Bin == remove_checksum(add_checksum(Bin))...

对于传出请求,原则是相同的。因此,这里不再对此作进一步的描述。完整的拦截器模块如下所示:

-module(myInterceptor). %% Interceptor functions. -export([new_out_connection/3, new_in_connection/3, closed_in_connection/1, closed_out_connection/1, in_request_encoded/6, in_reply_encoded/6, out_reply_encoded/6, out_request_encoded/6, in_request/6, in_reply/6, out_reply/6, out_request/6]). new_in_connection(Arg, Host, Port) -> %% Since we only use one interceptor we do not care about the %% input Arg since it is set do undefined by Orber. case ets:lookup(in_access_table, Host) of [] -> %% We may want to log the Host/Port to see if someone tried %% to hack in to our system. exit("Access not granted" [{Host, ObjTable, ChecksumModule}] -> {ObjTable, ChecksumModule} end. new_out_connection(Arg, Host, Port) -> case ets:lookup(out_access_table, Host) of [] -> exit("Access not granted" [{Host, ObjTable, ChecksumModule}] -> {ObjTable, ChecksumModule} end. in_request_encoded{_, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) -> NewBin = ChecksumModule:remove_checksum(Bin), {NewBin, Extra}. in_request{ObjTable, _}, ObjKey, Ctx, Op, Params, Extra) -> case ets:lookup(ObjTable, {ObjKey, Op}) of [] -> exit("Client tried to invoke illegal operation" [SomeData] -> {Params, Extra} end. out_reply(_, _, _, _, Reply, Extra) -> {Reply, Extra}. out_reply_encoded{_, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) -> NewBin = ChecksumModule:add_checksum(Bin), {NewBin, Extra}. out_request{ObjTable, _}, ObjKey, Ctx, Op, Params, Extra) -> case ets:lookup(ObjTable, {ObjKey, Op}) of [] -> exit("Client tried to invoke illegal operation" [SomeData] -> {Params, Extra} end. out_request_encoded{_, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) -> NewBin = ChecksumModule:add_checksum(Bin), {NewBin, Extra}. in_reply_encoded{_, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) -> NewBin = ChecksumModule:remove_checksum(Bin), {NewBin, Extra}. in_reply(_, _, _, _, Reply, Extra) -> {Reply, Extra}. closed_in_connection(Arg) -> %% Nothing to clean up. Arg. closed_out_connection(Arg) -> %% Nothing to clean up. Arg.

还可以使用拦截器进行调试,例如,打印调用哪些对象和操作,使用哪些参数和操作的结果。结合配置参数orber_debug_level找出什么问题或仅仅记录流量是相当容易的。