zoukankan      html  css  js  c++  java
  • 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存储对应关系:

    1 private final Map<String, BlockTokenSecretManager> map = 
    2     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的方法:

     1  /** Generate an block token for current user */
     2   public Token<BlockTokenIdentifier> generateToken(ExtendedBlock block,
     3       EnumSet<AccessMode> modes) throws IOException {
     4     UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
     5     String userID = (ugi == null ? null : ugi.getShortUserName());
     6     LOG.info("sqy test!"+"ugi="+ugi+",userID="+userID);
     7     return generateToken(userID, block, modes);
     8   }
     9 
    10   /** Generate a block token for a specified user */
    11   public Token<BlockTokenIdentifier> generateToken(String userId,
    12       ExtendedBlock block, EnumSet<AccessMode> modes) throws IOException {
    13     BlockTokenIdentifier id = new BlockTokenIdentifier(userId, block
    14         .getBlockPoolId(), block.getBlockId(), modes);
    15     return new Token<BlockTokenIdentifier>(id, this);
    16   }

          

          经过Kerberos和权限检查之后,Namenode就需要返回给用户块信息了。下面只讲主要的实现方法,调用BlockManager#createLocatedBlock(),该方法主要做了两件事:创建LocatedBlock和产生BlockToken。

    (1)进入BlockManager类

     1  private LocatedBlock createLocatedBlock(final BlockInfoContiguous blk, final long pos,
     2     final BlockTokenSecretManager.AccessMode mode) throws IOException {
     3     //获取BlockID、BlockPoolID、userID、位置pos
     4     final LocatedBlock lb = createLocatedBlock(blk, pos);
     5     if (mode != null) {
     6       //设置块令牌
     7       setBlockToken(lb, mode);
     8     }
     9     return lb;
    10   }

          由于我们这里主要讲块访问令牌的建立使用过程,创建LocatedBlock获取块信息这部分就不展开讲了。mode=AccessMode.READ,进入到setBlockToken方法中。

     1  public void setBlockToken(final LocatedBlock b,
     2       final BlockTokenSecretManager.AccessMode mode) throws IOException {
     3     // 如果开启BlockToken认证功能,这里是在hdfs-site.xml文件中配置的。
     4     if (isBlockTokenEnabled()) {
     5       // Use cached UGI if serving RPC calls.
     6       b.setBlockToken(blockTokenSecretManager.generateToken(
     7           NameNode.getRemoteUser().getShortUserName(),
     8           b.getBlock(), EnumSet.of(mode)));
     9     }    
    10   }

     (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);
      }

           产生返回的块令牌信息的实现:

     1  /**
     2    * Construct a token given a token identifier and a secret manager for the
     3    * type of the token identifier.
     4    * @param id the token identifier
     5    * @param mgr the secret manager
     6    */
     7   public Token(T id, SecretManager<T> mgr) {
     8     password = mgr.createPassword(id); // 设置令牌过期时间和keyId
     9     identifier = id.getBytes();
    10     kind = id.getKind();
    11     service = new Text();
    12   }

    Block Token的验证

          由之前的理论知识可知,namenode返回给用户块访问令牌,用户根据块信息和块访问令牌去datanode请求文件信息。因此块访问令牌的验证是在datanode发生的,根据代码追踪可知是在DataXceiver类,该类执行了各种block操作处理方法,而在readBlock、writeBlock中就包含了Block Token的验证操作。下面以readBlock方法为例来就行说明。

    (1)进入DataXceiver类

     1 public void readBlock(final ExtendedBlock block,
     2       final Token<BlockTokenIdentifier> blockToken,
     3       final String clientName,
     4       final long blockOffset,
     5       final long length,
     6       final boolean sendChecksum,
     7       final CachingStrategy cachingStrategy) throws IOException {
     8     previousOpClientName = clientName;
     9     long read = 0;
    10     updateCurrentThreadName("Sending block " + block);
    11     OutputStream baseStream = getOutputStream();
    12     DataOutputStream out = getBufferedOutputStream();
    13     //进行Token READ访问模式的验证
    14     checkAccess(out, true, block, blockToken,
    15         Op.READ_BLOCK, BlockTokenSecretManager.AccessMode.READ);
    16   ......

          进入到checkAccess方法中。

     1 private void checkAccess(OutputStream out, final boolean reply, 
     2       final ExtendedBlock blk,
     3       final Token<BlockTokenIdentifier> t,
     4       final Op op,
     5       final BlockTokenSecretManager.AccessMode mode) throws IOException {
     6     checkAndWaitForBP(blk);
     7     //判断是否启用BlockToken验证
     8     if (datanode.isBlockTokenEnabled) {
     9       if (LOG.isDebugEnabled()) {
    10         LOG.debug("Checking block access token for block '" + blk.getBlockId()
    11             + "' with mode '" + mode + "'");
    12       }
    13       try {
    14         //进行BlockToken验证
    15         datanode.blockPoolTokenSecretManager.checkAccess(t, null, blk, mode);
    16       } catch(InvalidToken e) {
    17       .....

    (2)进入BlockPoolTokenSecretManager类

    1  public Token<BlockTokenIdentifier> generateToken(ExtendedBlock b,
    2       EnumSet<AccessMode> of) throws IOException {
    3     return get(b.getBlockPoolId()).generateToken(b, of);
    4   }

    (3)进入BlockTokenSecretManager类

     1 public void checkAccess(Token<BlockTokenIdentifier> token, String userId,
     2       ExtendedBlock block, AccessMode mode) throws InvalidToken {
     3     BlockTokenIdentifier id = new BlockTokenIdentifier();
     4     try {
     5       //从输入流读取参数到tokenID,对其反序列化
     6       id.readFields(new DataInputStream(new ByteArrayInputStream(token
     7           .getIdentifier())));
     8     } catch (IOException e) {
     9       throw new InvalidToken(
    10           "Unable to de-serialize block token identifier for user=" + userId
    11               + ", block=" + block + ", access mode=" + mode);
    12     }
    13     //验证块令牌中的相关信息(userID、blockID、BlockPoolID、ExpiryDate、mode 
    14 checkAccess(id, userId, block, mode);
    15 .....

     结论  

         以上就是Block Token产生、验证的整个过程。不过是否开启Block Token验证是需要在hdfs-site.xml文件中配置的,默认是false。

        dfs.block.access.token.enable
    

      

  • 相关阅读:
    使用Distinct()内置方法对List集合的去重 问题
    TCP连接与HTTP请求
    ASP.NET MVC 使用 Authorize 属性过滤器验证用户是否已登录
    C#进阶系列——WebApi 跨域问题解决方案:CORS
    关于设计模式的六大原则
    C# WebApi 接口传参详解
    数据库数据流量太大-问题诊断
    docker的build生成镜像和启动container
    docker生成dotnet core镜像
    NET Core 源码浏览站点工具
  • 原文地址:https://www.cnblogs.com/qiuyuesu/p/6724441.html
Copyright © 2011-2022 走看看