Client.Connection.setupIOStreams()
--->发送连接上下文
writeConnectionContext(remoteId, authMethod);
发送的 IpcConnectionContextProto 里包含了UserGroupInformation
/**
* Spec for UserInformationProto is specified in ProtoUtil#makeIpcConnectionContext
*/
message UserInformationProto {
optional string effectiveUser = 1;
optional string realUser = 2;
}
/**
* The connection context is sent as part of the connection establishment.
* It establishes the context for ALL Rpc calls within the connection.
*/
message IpcConnectionContextProto {
// UserInfo beyond what is determined as part of security handshake
// at connection time (kerberos, tokens etc).
optional UserInformationProto userInfo = 2;
// Protocol name for next rpc layer.
// The client created a proxy with this protocol name
optional string protocol = 3;
}
在Server端的Connection.processOneRpc的过程中
private void processOneRpc(ByteBuffer bb)
throws IOException, InterruptedException {
// exceptions that escape this method are fatal to the connection.
// setupResponse will use the rpc status to determine if the connection
// should be closed.
int callId = -1;
int retry = RpcConstants.INVALID_RETRY_COUNT;
try {
final RpcWritable.Buffer buffer = RpcWritable.Buffer.wrap(bb);
final RpcRequestHeaderProto header =
getMessage(RpcRequestHeaderProto.getDefaultInstance(), buffer);
callId = header.getCallId();
retry = header.getRetryCount();
if (LOG.isDebugEnabled()) {
LOG.debug(" got #" + callId);
}
checkRpcHeaders(header);
if (callId < 0) { // callIds typically used during connection setup
//处理连接请求,ConnectionContext就是在这个方法里处理的
processRpcOutOfBandRequest(header, buffer);
} else if (!connectionContextRead) {
throw new FatalRpcServerException(
RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
"Connection context not established");
} else {
processRpcRequest(header, buffer);
}
}
/**
* Establish RPC connection setup by negotiating SASL if required, then
* reading and authorizing the connection header
* @param header - RPC header
* @param buffer - stream to request payload
* @throws RpcServerException - setup failed due to SASL
* negotiation failure, premature or invalid connection context,
* or other state errors. This exception needs to be sent to the
* client.
* @throws IOException - failed to send a response back to the client
* @throws InterruptedException
*/
private void processRpcOutOfBandRequest(RpcRequestHeaderProto header,
RpcWritable.Buffer buffer) throws RpcServerException,
IOException, InterruptedException {
final int callId = header.getCallId();
if (callId == CONNECTION_CONTEXT_CALL_ID) {
// SASL must be established prior to connection context
if (authProtocol == AuthProtocol.SASL && !saslContextEstablished) {
throw new FatalRpcServerException(
RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
"Connection header sent during SASL negotiation");
}
//处理ConnectionContext, 设置UserGroupInformation
// read and authorize the user
processConnectionContext(buffer);
} else if (callId == AuthProtocol.SASL.callId) {
// if client was switched to simple, ignore first SASL message
if (authProtocol != AuthProtocol.SASL) {
throw new FatalRpcServerException(
RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
"SASL protocol not requested by client");
}
saslReadAndProcess(buffer);
} else if (callId == PING_CALL_ID) {
LOG.debug("Received ping message");
}
设置user为协议设置的用户,如果底层是通过Sasl 传输的,realUser是拥有kerbos认证的用户,它代理proxyUser执行操作。
/** Reads the connection context following the connection header
* @throws RpcServerException - if the header cannot be
* deserialized, or the user is not authorized
*/
private void processConnectionContext(RpcWritable.Buffer buffer)
throws RpcServerException {
// allow only one connection context during a session
if (connectionContextRead) {
throw new FatalRpcServerException(
RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
"Connection context already processed");
}
connectionContext = getMessage(IpcConnectionContextProto.getDefaultInstance(), buffer);
protocolName = connectionContext.hasProtocol() ? connectionContext
.getProtocol() : null;
//获取连接上下文中的UserGroupInformation
UserGroupInformation protocolUser = ProtoUtil.getUgi(connectionContext);
//如果没有启动安全,Ugi就直接是传过来的用户
if (authProtocol == AuthProtocol.NONE) {
user = protocolUser;
} else {
//Ugi在processSasl的过程中,根据sasl的authorizationId设置的,参考方法 Conneciton.saslProcess()方法,里面会调用Connection.getAuthorizedUgi()方法,根据salsServer的authorizationId 创建
// user is authenticated
user.setAuthenticationMethod(authMethod);
//Now we check if this is a proxy user case. If the protocol user is
//different from the 'user', it is a proxy user scenario. However,
//this is not allowed if user authenticated with DIGEST.
if ((protocolUser != null)
&& (!protocolUser.getUserName().equals(user.getUserName()))) {
if (authMethod == AuthMethod.TOKEN) {
// Not allowed to doAs if token authentication is used
throw new FatalRpcServerException(
RpcErrorCodeProto.FATAL_UNAUTHORIZED,
new AccessControlException("Authenticated user (" + user
+ ") doesn't match what the client claims to be ("
+ protocolUser + ")"));
} else {
// Effective user can be different from authenticated user
// for simple auth or kerberos auth
// The user is the real user. Now we create a proxy user
UserGroupInformation realUser = user;
user = UserGroupInformation.createProxyUser(protocolUser
.getUserName(), realUser);
}
}
}
authorizeConnection();
// don't set until after authz because connection isn't established
connectionContextRead = true;
if (user != null) {
connectionManager.incrUserConnections(user.getShortUserName());
}
}