Block Token 原理分析
介绍
文件权限检查由NameNode执行,而不是DataNode执行。 默认情况下,任何客户端都可以访问只有其块ID的任何块。 为了解决这个问题,Hadoop引入了块访问令牌的概念。 块访问令牌由NameNode生成,并在DataNode端进行合法性验证。块访问令牌作为Hadoop数据传输协议的一部分或通过HTTP查询参数来呈现。一个典型的应用场景如下:一个客户端向NameNode发送文件读请求,NameNode验证该用户具有文件读权限后,将文件对应的所有数据块的ID、位置以及数据块访问令牌发送给客户端;当客户端需要读取某个数据块时,将数据块ID和数据块访问令牌发送给对应的DataNode。由于NameNode已经通过心跳将密钥发送给各个DataNode,因此DataNode可以对数据块进行安全验证,而只有通过安全验证的访问请求才可以获取数据块。
Block Token产生和验证的过程如下:
(1)Namenode经过对客户的身份验证和访问权限验证之后,返回块位置以及块访问令牌。
(2)客户端给Datanode发送块ID以及块访问令牌请求数据。
(3)Datanode经过验证块访问令牌之后返回给客户端请求的数据。
源码分析
用户使用Block Token访问数据的流程图如下:

Block Token的产生
由代码追踪可知Block Token是调用BlockPoolTokenSecretManager类产生的,但实际产生Block Token的操作是由BlockTokenSecretManager类执行的,该类与BlockPoolTokenSecretManager类的关系如下:
BlockPoolTokenSecretManager包含BlockTokenSecretManager,并且每一个blockPool对应一个BlockTokenSecretManager
实际用map存储对应关系:
private final Map<String, BlockTokenSecretManager> map =
new HashMap<String, BlockTokenSecretManager>();
所以先调用BlockPoolTokenSecretManager类的方法获取BlockPoolId找到对应的BlockTokenSecretManager。
/**
* See {@link BlockTokenSecretManager#generateToken(ExtendedBlock, EnumSet)}
*/
public Token<BlockTokenIdentifier> generateToken(ExtendedBlock b,
EnumSet<AccessMode> of) throws IOException {
return get(b.getBlockPoolId()).generateToken(b, of);
}
进入实际产生BlockToken的方法:
/** Generate an block token for current user */
public Token<BlockTokenIdentifier> generateToken(ExtendedBlock block,
EnumSet<AccessMode> modes) throws IOException {
UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
String userID = (ugi == null ? null : ugi.getShortUserName());
LOG.info("sqy test!"+"ugi="+ugi+",userID="+userID);
return generateToken(userID, block, modes);
} /** Generate a block token for a specified user */
public Token<BlockTokenIdentifier> generateToken(String userId,
ExtendedBlock block, EnumSet<AccessMode> modes) throws IOException {
BlockTokenIdentifier id = new BlockTokenIdentifier(userId, block
.getBlockPoolId(), block.getBlockId(), modes);
return new Token<BlockTokenIdentifier>(id, this);
}
经过Kerberos和权限检查之后,Namenode就需要返回给用户块信息了。下面只讲主要的实现方法,调用BlockManager#createLocatedBlock(),该方法主要做了两件事:创建LocatedBlock和产生BlockToken。
(1)进入BlockManager类
private LocatedBlock createLocatedBlock(final BlockInfoContiguous blk, final long pos,
final BlockTokenSecretManager.AccessMode mode) throws IOException {
//获取BlockID、BlockPoolID、userID、位置pos
final LocatedBlock lb = createLocatedBlock(blk, pos);
if (mode != null) {
//设置块令牌
setBlockToken(lb, mode);
}
return lb;
}
由于我们这里主要讲块访问令牌的建立使用过程,创建LocatedBlock获取块信息这部分就不展开讲了。mode=AccessMode.READ,进入到setBlockToken方法中。
public void setBlockToken(final LocatedBlock b,
final BlockTokenSecretManager.AccessMode mode) throws IOException {
// 如果开启BlockToken认证功能,这里是在hdfs-site.xml文件中配置的。
if (isBlockTokenEnabled()) {
// Use cached UGI if serving RPC calls.
b.setBlockToken(blockTokenSecretManager.generateToken(
NameNode.getRemoteUser().getShortUserName(),
b.getBlock(), EnumSet.of(mode)));
}
}
(2)进入BlockTokenSecretManager类
在BlockManager类中调用generateToken方法创建令牌之前,会先创建BlockTokenSecretManager类的实例对象blockTokenSecretManager,设置块访问密钥更新间隔时间、块访问令牌的生命周期、加密算法以及生成了密钥。
/** Generate a block token for a specified user */
public Token<BlockTokenIdentifier> generateToken(String userId,
ExtendedBlock block, EnumSet<AccessMode> modes) throws IOException {
//生成TokenID
BlockTokenIdentifier id = new BlockTokenIdentifier(userId, block
.getBlockPoolId(), block.getBlockId(), modes);
//返回块访问令牌
return new Token<BlockTokenIdentifier>(id, this);
}
产生返回的块令牌信息的实现:
/**
* Construct a token given a token identifier and a secret manager for the
* type of the token identifier.
* @param id the token identifier
* @param mgr the secret manager
*/
public Token(T id, SecretManager<T> mgr) {
password = mgr.createPassword(id); // 设置令牌过期时间和keyId
identifier = id.getBytes();
kind = id.getKind();
service = new Text();
}
Block Token的验证
由之前的理论知识可知,namenode返回给用户块访问令牌,用户根据块信息和块访问令牌去datanode请求文件信息。因此块访问令牌的验证是在datanode发生的,根据代码追踪可知是在DataXceiver类,该类执行了各种block操作处理方法,而在readBlock、writeBlock中就包含了Block Token的验证操作。下面以readBlock方法为例来就行说明。
(1)进入DataXceiver类
public void readBlock(final ExtendedBlock block,
final Token<BlockTokenIdentifier> blockToken,
final String clientName,
final long blockOffset,
final long length,
final boolean sendChecksum,
final CachingStrategy cachingStrategy) throws IOException {
previousOpClientName = clientName;
long read = 0;
updateCurrentThreadName("Sending block " + block);
OutputStream baseStream = getOutputStream();
DataOutputStream out = getBufferedOutputStream();
//进行Token READ访问模式的验证
checkAccess(out, true, block, blockToken,
Op.READ_BLOCK, BlockTokenSecretManager.AccessMode.READ);
......
进入到checkAccess方法中。
private void checkAccess(OutputStream out, final boolean reply,
final ExtendedBlock blk,
final Token<BlockTokenIdentifier> t,
final Op op,
final BlockTokenSecretManager.AccessMode mode) throws IOException {
checkAndWaitForBP(blk);
//判断是否启用BlockToken验证
if (datanode.isBlockTokenEnabled) {
if (LOG.isDebugEnabled()) {
LOG.debug("Checking block access token for block '" + blk.getBlockId()
+ "' with mode '" + mode + "'");
}
try {
//进行BlockToken验证
datanode.blockPoolTokenSecretManager.checkAccess(t, null, blk, mode);
} catch(InvalidToken e) {
.....
(2)进入BlockPoolTokenSecretManager类
public Token<BlockTokenIdentifier> generateToken(ExtendedBlock b,
EnumSet<AccessMode> of) throws IOException {
return get(b.getBlockPoolId()).generateToken(b, of);
}
(3)进入BlockTokenSecretManager类
public void checkAccess(Token<BlockTokenIdentifier> token, String userId,
ExtendedBlock block, AccessMode mode) throws InvalidToken {
BlockTokenIdentifier id = new BlockTokenIdentifier();
try {
//从输入流读取参数到tokenID,对其反序列化
id.readFields(new DataInputStream(new ByteArrayInputStream(token
.getIdentifier())));
} catch (IOException e) {
throw new InvalidToken(
"Unable to de-serialize block token identifier for user=" + userId
+ ", block=" + block + ", access mode=" + mode);
}
//验证块令牌中的相关信息(userID、blockID、BlockPoolID、ExpiryDate、mode)
checkAccess(id, userId, block, mode);
.....
结论
以上就是Block Token产生、验证的整个过程。不过是否开启Block Token验证是需要在hdfs-site.xml文件中配置的,默认是false。
dfs.block.access.token.enable
Block Token 原理分析的更多相关文章
- [转]Handler MessageQueue Looper消息循环原理分析
Handler MessageQueue Looper消息循环原理分析 Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...
- WebViewJavascriptBridge 原理分析
WebViewJavascriptBridge 原理分析 网上好多都是在介绍 WebViewJavascriptBridge如何使用,这篇文章就来说说 WebViewJavascriptBridge ...
- Java NIO使用及原理分析 (四)
在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O.通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至 ...
- tomcat原理分析与简单实现
tomcat原理分析与简单实现 https://blog.csdn.net/u014795347/article/details/52328221 2016年08月26日 14:48:18 卫卫羊习习 ...
- Java NIO使用及原理分析 (四)(转)
在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O.通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至 ...
- OAuth认证协议原理分析及同步消息到Twitter和Facebook使用方法
OAuth有什么用?为什么要使用OAuth? twitter或豆瓣用户一定会发现,有时候,在别的网站,点登录后转到 twitter登录,之后转回原网站,你会发现你已经登录此网站了,这种网站就是这个效果 ...
- Redis数据持久化机制AOF原理分析一---转
http://blog.csdn.net/acceptedxukai/article/details/18136903 http://blog.csdn.net/acceptedxukai/artic ...
- 原理剖析-Netty之服务端启动工作原理分析(下)
一.大致介绍 1.由于篇幅过长难以发布,所以本章节接着上一节来的,上一章节为[原理剖析(第 010 篇)Netty之服务端启动工作原理分析(上)]: 2.那么本章节就继续分析Netty的服务端启动,分 ...
- Hadoop数据管理介绍及原理分析
Hadoop数据管理介绍及原理分析 最近2014大数据会议正如火如荼的进行着,Hadoop之父Doug Cutting也被邀参加,我有幸听了他的演讲并获得亲笔签名书一本,发现他竟然是左手写字,当然这个 ...
随机推荐
- 1657: [Usaco2006 Mar]Mooo 奶牛的歌声
1657: [Usaco2006 Mar]Mooo 奶牛的歌声 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 526 Solved: 365[Submi ...
- cassandra简单介绍与基本操作
项目中用到了cassandra,用来存储海量数据,且要有高效的查询:本博客就进行简单的介绍和进行一些基本的操作 一.使用场景: 是一款分布式的结构化数据存储方案(NoSql数据库),存储结构比Key- ...
- 使用Git将本地仓库与GitHub远程仓库相关联
这篇文章详细讲解了如何生成SSH,并链接到GitHub,123..... 1.如果你的GitHub里面没有仓库,就自己生成一个,如图所示 2.如果你有自己仓库,想在电脑本地新建一个,你需要将GitHu ...
- spring-AOP-ProxyFactoryBean代理的实例
1.一个代理模式的实例 通过 Proxy类进行代理 wait.java //定义一个接口 public interface wait { void say(); } //目标对象实现接口并重写方法 p ...
- “倔驴”一个h5小游戏的实现和思考(码易直播)——总结与整理
3月23日晚上8点半(中国队火拼韩国的时候),做了一期直播分享.15年做的一个小游戏,把核心代码拿出来,现场讲写了一遍,结果后面翻车了,写错了两个地方,导致运行效果有点问题,直播边说话边写代码还真不一 ...
- iOS开发之数据存储之XML属性列表(plist)归档
1.概述 “归档”意思是持久化存储数据.plist文件是一种XML格式的文件,拓展名为plist.如果对象是NSString.NSDictionary.NSArray.NSData.NSNumber等 ...
- windows和linux删除文件方法
Windows下bat文件内容如下:复制代码 代码如下: @echo offforfiles -p "D:\servers\apache2.2\logs" -s -m *.log ...
- node.js下mongoose简单操作实例
Mongoose API : http://mongoosejs.com/docs/api.html // mongoose 链接var mongoose = require('mongoose'); ...
- python复习。知识点小记
1.对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符: >>> ord('A') >>> ord('中' ...
- ASP.NET Core MVC 源码学习:详解 Action 的激活
前言 在 上一篇 文章中,我们已经学习了 ASP.NET Core MVC 的启动流程,那么 MVC 在启动了之后,当请求到达过来的时候,它是怎么样处理的呢? 又是怎么样把我们的请求准确的传达到我们的 ...