# 配置Spark动态脱敏
#### 操作场景
动态脱敏是一种安全特性，允许你根据用户的权限或角色对数据进行部分或全部屏蔽，从而保护敏感数据不被未经授权的用户访问。
Spark动态脱敏开启后，配置脱敏列的数据可以参与计算，计算结果输出时不可见，在集群内脱敏策略会根据血缘关系自动传递，更大地发挥数据的价值同时，保障数据的隐私性。
典型应用场景：
- 金融行业：对客户身份证号、手机号、银行卡号等敏感字段进行动态脱敏，满足合规审计要求。
- 多部门数据共享：不同部门用户查询同一张表时，根据角色权限看到不同脱敏级别的数据。
- 个人信息保护：对用户姓名、地址等PII（Personally Identifiable Information）数据进行脱敏，符合数据安全法规要求。
 
#### 约束与限制
- 本章节仅适用于MRS 3.3.1-LTS及之后版本。
- 仅支持MRS安全版本集群（启用Kerberos认证）。
- 管理面提交作业方式不支持开启动态脱敏特性。
- 不支持Hudi表、Iceberg表的脱敏。
- 不支持非SQL使用方法的脱敏。
- 不支持涉及直接读写HDFS的操作的脱敏。
- 不支持复杂类型array、map、struct的脱敏。
- 只支持spark-beeline（JDBC 连接）方式提交Spark作业。
- 脱敏策略传递时，若目标表已有脱敏策略且与源表有冲突，则目标表脱敏策略强制重置为Custom："\*\*\*"。
- MRS 3..6.0之前版本：当前仅支持int、char、varchar、date、decimal、float、bigint、timestamp 、tinyint、smallint、double、string、binary数据类型的脱敏，其中int、date、decimal、float、bigint、timestamp 、tinyint、smallint、double类型配置脱敏策略后，spark-beeline查询结果存在与策略预期不一致的现象，但查询结果非原始值，如需要与策略结果保持一致，则推荐使用"Nullify"脱敏策略。
- MRS 3.6.0-LTS及之后版本：当前仅支持int、char、varchar、date、decimal、float、bigint、timestamp 、tinyint、smallint、double、string、binary数据类型的脱敏，其中int、date、decimal、float、bigint、timestamp 、tinyint、smallint、double、binary类型配置脱敏策略后，spark-beeline查询结果为null，如需要与策略结果保持一致，则推荐使用"Nullify"脱敏策略。
- 对于不支持的数据类型，如果配置了脱敏策略或输出列涉及脱敏传递，最终都按"Nullify"脱敏策略处理。
- MRS 3.6.0-LTS及之后版本：对于操作符、UDF返回的类型为不支持的类型时，如果在动态脱敏处理范围，统一按"Nullify"脱敏策略处理。对于不支持的类型，可能出现空指针问题，如：使用distinct操作符，入参大于2时，返回结果为struct类型，如果入参中包含脱敏涉及字段，在spark-beeline场景下，会出现空指针问题。
- 管理面提交作业方式不支持开启动态脱敏特性。
 
#### 配置Spark动态脱敏
1. 修改JDBCServer实例配置。登录FusionInsight Manager页面，选择"集群 \> 服务 \> Spark \> 配置 \> 全部配置 \> JDBCServer（角色）"，修改如下参数，保存配置，并重启Spark服务。 
   - 使用Ranger鉴权场景，在参数"custom"中添加如下自定义参数：
     
     | 参数                                          | 值     | 参数说明                                                                                                                                                                                                                                                                                   |
     |:---|:---|:---|
     | spark.dynamic.masked.enabled               | true  | 是否启用动态脱敏，启用该功能可以增强数据安全性。 - true：启用动态数据屏蔽功能。  - false：禁用动态数据屏蔽功能。                                                       |
     | spark.ranger.plugin.authorization.enable | true | 是否开启Ranger鉴权功能。启用该功能可以增强数据访问的安全性，如果需要细粒度的访问控制，建议启用此功能并进行相应的配置。 - true：启用Ranger鉴权功能。  - false：禁用Ranger鉴权功能。   |
        
     修改如下参数值：
     
     | 参数名称                                | 值   | 参数说明                                                                                                                                                                                                                                                                                                    |
     |:---|:---|:---|
     | spark.ranger.plugin.masking.enable | true  | 配置是否启用Ranger插件的列脱敏功能。启用该功能可以增强数据的安全性，但需要确保 Ranger服务已经正确配置，并且定义了合适的屏蔽策略。 - true：启用Ranger插件的列脱敏功能。  - false：禁用Ranger插件的列脱敏功能。   |
     | spark.sql.authorization.enabled    | true | 安全模式下开启SQL鉴权，启用此功能后，Spark会根据用户的角色和权限来执行SQL查询，从而提供细粒度的访问控制。 - true：启用SQL鉴权功能。  - false：禁用SQL鉴权功能。                                |
        
     
   
   - 对于不使用Ranger鉴权，依然使用Hive元数据鉴权场景，在参数"custom"中添加如下自定义参数：
     
     | 参数名称                                      | 值     | 参数说明                                                                                                                                                                                                                                                                               |
     |:---|:---|:---|
     | spark.ranger.plugin.use.hive.acl.enable  | true | 是否启用Spark使用Hive的ACL进行授权检查。 - true：启用Spark使用Hive的ACL进行授权检查。  - false：禁用Spark使用Hive的ACL进行授权检查。                    |
     | spark.dynamic.masked.enabled            | true   | 是否启用动态脱敏，启用该功能可以增强数据安全性。 - true：启用动态数据屏蔽功能。  - false：禁用动态数据屏蔽功能。                                      |
     | spark.ranger.plugin.authorization.enable | true    | 是否开启Ranger鉴权功能。启用该功能可以增强数据访问的安全性，如果需要细粒度的访问控制，建议启用此功能并进行相应的配置。 - true：启用Ranger鉴权功能。  - false：禁用Ranger鉴权功能。   |
        
     修改如下参数值：
     
     | 参数名称                               | 值     | 参数说明                                                                                                                                                                                                                                                  |
     |:---|:---|:---|
     | spark.ranger.plugin.masking.enable | true | 配置是否启用Ranger插件的列脱敏功能。 - true：启用Ranger插件的列脱敏功能。  - false：禁用Ranger插件的列脱敏功能。   |
        
     
   
   
   不使用Ranger鉴权，使用Hive元数据鉴权场景，如果Ranger中未完成Hive策略初始化，需要做以下操作：
   1. 开启组件Hive的"Ranger鉴权"的功能，重启Hive组件+Spark组件。
   
   2. 开启组件Spark的"Ranger鉴权"的功能，重启Spark组件。
   
   3. 关闭组件Hive的"Ranger鉴权"的功能，重启Hive组件。
   
   4. 关闭组件Spark的"Ranger鉴权"的功能，重启Spark组件。
   
   
   
   5. 登录Ranger WebUI界面，如果"HADOOP SQL"下存在Hive组件表示已经完成Hive策略初始化，不存在则未初始化。
   
   6. 如果集群同时安装了HetuEngine组件，且需要Spark动态脱敏触发策略传递时自动同步更新Ranger、HetuEngine空间的脱敏策略，需要将参数"spark.dynamic.masked.hetu.policy.sync.update.enable"设置为"true"，且需将内置用户Spark的Ranger用户类型调整为Admin用户类型。
    
   
   
2. 登录Spark客户端节点，执行如下命令： 
   进入客户端目录：
   ```
   cd 客户端安装目录
   ```
   加载环境变量：
   ```
   source bigdata_env
   ```
   加载组件环境变量：
   ```
   source Spark/component_env
   ```
   安全模式执行安全认证，普通模式无需执行：
   ```
   kinit test
   ```
   输入密码完成认证（首次登录需要修改密码）
   
   
3. 使用Spark的beeline命令提交任务，创建Spark表： 
   ```
   spark-beeline
   create table sparktest(a int, b string);
   insert into sparktest values (1,"test01"), (2,"test02");
   ```
   
   
4. 参考[添加Spark的Ranger访问权限策略](https://support.huaweicloud.com/cmpntguide-lts-mrs/mrs_01_1860.html)，给sparktest表配置脱敏策略，验证脱敏是否生效：
   
   ```
   select  * from sparktest;
   ```
   ![](https://support.huaweicloud.com/cmpntguide-lts-mrs/zh-cn_image_0000002061926864.png "点击放大")
   
   
5. 验证脱敏策略传递： 
   ```
   create table sparktest02 as select * from sparktest;
   select * from sparktest02;
   ```
   ![](https://support.huaweicloud.com/cmpntguide-lts-mrs/zh-cn_image_0000002098163877.png "点击放大")
   出现以上结果表示，动态脱敏配置已生效，登录ranger脱敏策略管理界面可以看到有自动生成的表sparktest02的脱敏策略。
   
   
 
#### 特性说明
- Spark动态脱敏后返回字段类型与输出列的schema类型一致，无法保持一致的使用null表示脱敏后的结果。

- 支持不脱敏UDF名单和强制hash脱敏UDF名单配置。
  
  | 参数名称                              | 说明                                                                                                                              | 取值示例                                                                     |
  |:---|:---|:---|
  | spark.sql.udf.hash.dynamic.mask | 开启动态脱敏后，使用Partial mask: show last 4、Partial mask: show first 4、Custom脱敏策略脱敏时，需要强制使用hash脱敏处理的函数名单。如果有多个函数需要强制使用hash脱敏处理，则以英文逗号隔开。 | substr,substring,left,right,concat,concat_ws,lpad,repeat,rpad,reverse |
  | spark.sql.udf.skip.dynamic.mask | 开启动态脱敏后，不需要脱敏处理的函数名单。如果有多个函数不需要脱敏处理，则以英文逗号隔开。                                                                                    | count                                                                   |
     
  配置的UDF函数名需要为执行计划真实使用的函数名。如left函数，执行时使用的是substring函数，所以配置spark.sql.udf.hash.dynamic.mask或spark.sql.udf.skip.dynamic.mask时需要配置substring，配置left不生效。
  可以使用explain语句获取执行时对应的函数名，如：
  ![](https://support.huaweicloud.com/cmpntguide-lts-mrs/zh-cn_image_0000002256226660.png "点击放大")
  spark.sql.udf.skip.dynamic.mask的优先级大于spark.sql.udf.hash.dynamic.mask。如果一个函数名同时配置在spark.sql.udf.skip.dynamic.mask和spark.sql.udf.hash.dynamic.mask中时，会按照spark.sql.udf.skip.dynamic.mask逻辑处理，不进行脱敏处理。
  

- Custom脱敏策略支持配置mask_partial函数：
  **mask_partial函数介绍**
  1. 处理数值类型的**mask_partial** 说明。
     ```
     mask_partial(column_name, mask_digital, mask_from[, mask_to])
     ```
     描述：针对数值类型数据（int、decimal、float、bigint、tinyint、smallint、double），将第mask_from到mask_to位的数字部分脱敏成mask_digital对应的数字。
     表1参数说明1 
     | 参数             | 说明                  |
     |:---|:---|
     | mask_to      | 允许缺省，缺省时即脱敏到数据结束位置。 |
     | mask_digital | 只能取\[1,9\]区间内的数字。   |
        
     返回值类型：与入参column_name数据类型相同。
     对于decimal、float、double类型，小数点参与计数，但不参与脱敏。如：列内容为:3.1415926，mask_partial('2',1,3），返回值：2.2415926。
     Spark-beeline场景下，使用**mask_partial**对decimal脱敏后，结果会不符合预期，但结果是脱敏的，非原始值。
     
  
  2. 处理字符类型的mask_partial说明。
     ```
     mask_partial(column_name , mask_char, mask_from[, mask_to])
     ```
     描述：针对字符类型数据（char, varchar,string），对照指定的输入输出格式，将第mask_from到mask_to位的数字部分脱敏成mask_char指定的字符。
     表2参数说明2 
     | 参数          | 说明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
     |:---|:---|
     | mask_char   | 脱敏字符，仅允许长度为1的任意字符。支持字符范围： - 大写字母：A, B, C, D, E, ..., Z  - 小写字母：a, b, c, d, e, ..., z  - 阿拉伯数字：0, 1, 2, 3, 4, 5, 6, 7, 8, 9  - 标点符号：句号（.）、逗号（,）、问号（?）、感叹号（!）、冒号（:）、分号（;）、引号（"）、括号（( 或 )，\[ 或 \]，{ 或 }）、连字符（-）  - 特殊符号：@ 符号 (@)、井号 、美元符号 ($)、百分号 (%)、和号 (\&)、星号 (\*)、加号 (+)、等号 (=)、波浪号 (\~)、下划线 (_)、竖线 (\|)   |
     | mask_from | 脱敏范围的起始位置，要求大于0。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
     | mask_to  | 脱敏范围的结束位置，允许缺省。缺省时，即脱敏到原始数据结束位置。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
        
     返回值类型：与入参column_name数据类型相同。
     
  
  
  
  3. 处理时间类型的**mask_partial** 说明。
     mask_partial(column_name, mask_digital, mask_from\[, mask_to\])
     描述：针对时间类型数据（Date、TimeStamp），将第mask_from到mask_to位的数字部分脱敏成mask_digital对应的数字。
     表3参数说明3 
     | 参数           | 说明                  |
     |:---|:---|
     | mask_to       | 允许缺省，缺省时即脱敏到数据结束位置。 |
     | mask_digital | 只能取\[1,9\]区间内的数字。    |
        
     返回值类型：与入参column_name数据类型相同。
     1. Date只支持年、月、日的脱敏。
     
     2. TimeStamp支持年、月、日、时、分、秒、毫秒/微秒/纳秒的脱敏。
        - Date、TimeStamp中的"-"、"/"、空格、":"参与计数，不脱敏。
        
        - Date、TimeStamp中，月、日的值脱敏后超过正常范围后将进行反转处理，例如： 月的值为02，脱敏后为33，最终值为33%12=9。
          日的值为10，脱敏后为44，最终值为44 % (脱敏后的年对应的脱敏后的月的天数) , 假设脱敏后的年为2024，脱敏后的月为9（2024年9月有30天)， 最终值为14。
          
         
     
     3. TimeStamp中的时、分、秒、毫秒/微秒/纳秒脱敏后，超过正常范围后，也进行反转处理。
        - 时的值为20，脱敏后为66，最终值为66%24=18。
        
        - 分的值为30，脱敏后为66，最终值为66%60=6。
        
        - 秒的值为50，脱敏后为66，最终值为66%60=6。
        
        - 毫秒/微秒/纳秒不涉及超出正常值范围场景。 例如：
          date类型脱敏：值为1920-01-03，使用mask_partial('2',5,8）脱敏后，结果为1920-10-03。
          TimeStamp类型脱敏：值为'2023-12-05 12:34:56.789' ，使用mask_partial('2',5,8）脱敏后，结果为'2023-10-05 12:34:56.789'。
          
         
       
   
  **mask_partial使用约束与限制说明**
  - MRS mask_partial的入参中不支持嵌套UDF。
  
  - mask_partial遇到数值、字符、时间之外的不支持的类型时，统一返回null。
  
  - mask_from从1开始计数，即mask_from要求是大于0的整数，小于1时，默认按照1处理。
  
  - mask_to可以不填，不填时，默认为对应字符的结束位置，mask_to可以为负数，-1表示字符的结束位置。
  
  - mask_to对应字符串的位置不能在mask_from之前，遇到该情况时，统一返回null。
  
  - mask_partial处理类型为数值类型时， mask_digital不属于\[1,9\]区间内的数字时，UDF函数返回null。
  
  - mask_partial处理类型为字符类型时， mask_char不支持该字符类型时，UDF函数返回null。
  
  - 遇到mask_partial编写错误、入参不支持、入参有误场景， 引擎直接发生异常。
  
  - 支持中文字符的脱敏
   
  **mask_partial使用介绍**
  在ranger上配置脱敏策略，Mask Conditions选择Custom，在自定义输入框中填写mask_partial(mask_digital, mask_from\[, mask_to\])，其中mask_digital需要使用单引号括起来，如'9'、'\*'。
  例如：mask_partial('\*',1,5)
  ![](https://support.huaweicloud.com/cmpntguide-lts-mrs/zh-cn_image_0000002170338369.png)
  ![](https://support.huaweicloud.com/cmpntguide-lts-mrs/public_sys-resources/note_3.0-zh-cn.png)
  Mask Conditions选择Custom，配置mask_partial脱敏函数时，第一个入参不需要指定列名，直接从mask_partial函数介绍中的第二个入参开始填写。
  
  
 
#### 相关文档
- 了解如何在Ranger中为Spark表配置访问权限和脱敏策略，具体请参考[添加Spark的Ranger访问权限策略](https://support.huaweicloud.com/cmpntguide-lts-mrs/mrs_01_1860.html)。
- 了解如何安装和使用Spark客户端，具体请参考[Spark客户端使用实践](https://support.huaweicloud.com/cmpntguide-lts-mrs/mrs_01_1929.html)。
- 如需了解Spark常用配置参数的含义和用法，具体请参考[Spark常用配置参数](https://support.huaweicloud.com/cmpntguide-lts-mrs/mrs_01_1931.html)。
- 在使用Spark过程中遇到常见问题时，请参考[Spark常见问题](https://support.huaweicloud.com/cmpntguide-lts-mrs/mrs_01_2002.html)。
 
