xampp php kafka,kafka 0.10.1.0 权限验证源码分析

初始化流程图

6e36499676831ae02986dc04b0c2174a.png

ChannelBuilders.create创建ChannelBuilder对应关系如下:

switch (securityProtocol) {

case SSL:

requireNonNullMode(mode, securityProtocol);

channelBuilder = new SslChannelBuilder(mode);

break;

case SASL_SSL:

case SASL_PLAINTEXT:

requireNonNullMode(mode, securityProtocol);

if (loginType == null)

throw new IllegalArgumentException("`loginType` must be non-null if `securityProtocol` is `" + securityProtocol + "`");

if (mode == Mode.CLIENT && clientSaslMechanism == null)

throw new IllegalArgumentException("`clientSaslMechanism` must be non-null in client mode if `securityProtocol` is `" + securityProtocol + "`");

channelBuilder = new SaslChannelBuilder(mode, loginType, securityProtocol, clientSaslMechanism, saslHandshakeRequestEnable);

break;

case PLAINTEXT:

case TRACE:

channelBuilder = new PlaintextChannelBuilder();

break;

default:

throw new IllegalArgumentException("Unexpected securityProtocol " + securityProtocol);

}

这里我们配置的listeners值是

listeners=SASL_PLAINTEXT://0.0.0.0:9092

故解析出来的protocol就是SASL_PLAINTEXT,相应的ChannelBuilder也就是SaslChannelBuilder

到这里,权限验证相关的组件算是构建完毕了,然后我们看当一个连接接进来的时候,这些组件是怎么工作的。

入口是Accepter.accept

10c628c6f8cf860280cb0f3fd58d6e52.png

接下来是Processor线程

5dd8320ed13cb9bf366f8f7067aa10a0.png

+ SaslChannelBuilder.buildChannel:

* 这里会根据构造SaslChannelBuilder时传进来的mode参数的不同选择构造SaslServerAuthenticator还是SaslClientAuthenticator,我们这里是服务端,当然是构造SaslServerAuthenticator.

* 构造好SaslServerAuthenticator后会调用它的configure函数,进行一些初始化配置。

* 把SaslServerAuthenticator对象作为参数传给KafkaChannel返回。

+ 这里注册好新的kafkaChannel后会调用poll函数完成一些IO的读写操作,而权限验证的处理就以这里为入口。

+ pollSelectionKeys函数会处理所有可完成连接,可读或可写的KafkaChannel。而权限验证部分则出现在KafkaChannel的准备阶段。

+ authenticate这个函数内会根据目前握手所处的状态的不同而做不同的处理,

* 首先是HANDSHAKE_REQUEST状态,调用handleKafkaRequest处理第一阶段的握手请求,解析出客户端发来的mechanism,

根据mechanism创建SaslServer,代码如下:

saslServer = Subject.doAs(subject, new PrivilegedExceptionAction() {

public SaslServer run() throws SaslException {

return Sasl.createSaslServer(saslMechanism, "kafka", host, configs, callbackHandler);

}

});

这里具体根据我们在jaas配置文件中的配置,最后是返回了一个PlainSaslServer,具体为什么返回了一个PlainSaslServer稍后讲。

* 然后下一状态是AUTHENTICATE,验证客户端发来的明文用户名和密码,调用了PlainSaslServer的evaluateResponse,代码如下

String[] tokens;

try {

tokens = new String(response, "UTF-8").split("\u0000");

} catch (UnsupportedEncodingException e) {

throw new SaslException("UTF-8 encoding not supported", e);

}

if (tokens.length != 3)

throw new SaslException("Invalid SASL/PLAIN response: expected 3 tokens, got " + tokens.length);

authorizationID = tokens[0];

String username = tokens[1];

String password = tokens[2];

if (username.isEmpty()) {

throw new SaslException("Authentication failed: username not specified");

}

if (password.isEmpty()) {

throw new SaslException("Authentication failed: password not specified");

}

if (authorizationID.isEmpty())

authorizationID = username;

try {

String expectedPassword = JaasUtils.jaasConfig(LoginType.SERVER.contextName(), JAAS_USER_PREFIX + username);

if (!password.equals(expectedPassword)) {

throw new SaslException("Authentication failed: Invalid username or password");

}

} catch (IOException e) {

throw new SaslException("Authentication failed: Invalid JAAS configuration", e);

}

所以这里可以根据我们的需求根据客户端传过来的username和password动态的去某一个数据源获取和匹配其合法性。

jaas配置文件:

KafkaServer {

org.apache.kafka.common.security.plain.PlainLoginModule required

username="xxxxx"

password="yyyyy"

user_xxxxx="yyyyy";

};

看到这里我们配置了一个org.apache.kafka.common.security.plain.PlainLoginModule

6f736b4c6a53c6eb19489fdee9f57648.png

上面讲到返回了一个PlainSaslServer,具体是怎么返回的呢?我们需要从SaslChannelBuilder的configure说起,上流程图:

+ LoginContext.init这里会初始化ConfigFile,读取jaas配置文件中的内容

+ LoginContext.login这里会实例化jaas配置文件中的PlainLoginModule,并依次调用其initialize和login函数,

其中initialize函数会把username和password配置到subject里面去,这个subject最终会通过LoginManager的subject函数在SaslChannelBuilder的buildChannel函数中获取到,设置到SaslClientAuthenticator中用于和其他服务器通讯验证使用。

+ 而PlainLoginModule可不仅仅只做了这一件事情,该类定义了一个静态块初始化代码,调用了PlainSaslServerProvider的initialize函数用于注册创建负责做PLAIN协议验证的类的工厂类PlainSaslServerFactory。代码如下:

protected PlainSaslServerProvider() {

super("Simple SASL/PLAIN Server Provider", 1.0, "Simple SASL/PLAIN Server Provider for Kafka");

super.put("SaslServerFactory." + PlainSaslServer.PLAIN_MECHANISM, PlainSaslServerFactory.class.getName());

}

我们看到注册的是一个SaslServerFactory.PLAIN -> org.apache.kafka.common.security.plain.PlainSaslServer.PlainSaslServerFactory的对应关系

而再看前面调用的Sasl的createSaslServer的代码:

String mechFilter = "SaslServerFactory." + mechanism;

Provider[] provs = Security.getProviders(mechFilter);

for (int j = 0; provs != null && j < provs.length; j++) {

className = provs[j].getProperty(mechFilter);

if (className == null) {

throw new SaslException("Provider does not support " +

mechFilter);

}

fac = (SaslServerFactory) loadFactory(provs[j], className);

if (fac != null) {

mech = fac.createSaslServer(

mechanism, protocol, serverName, props, cbh);

if (mech != null) {

return mech;

}

}

}

其中mechFilter的值即是SaslServerFactory.PLAIN,这里取到PlainSaslServerFactory然后调用createSaslServer方法返回了一个PlainSaslServer

综上,kafka内部的整个权限验证的初始化流程和验证逻辑已经比较清晰了(可能讲的比较乱,反正我是清晰了),但是我们发现,跟网上的其他自己编写LoginModule模块做验证的方式不同,kafka的PlainLoginModule这个类本身并没有做什么跟验证有关的逻辑,

只是做了一些初始化和注册provider的工作,而真正做权限验证的是从provider间接生产出来的PlainSaslServer类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值