自定义审批系统对接接口

本文为您介绍Dataphin BPMS支持对接第三方审批流,包括开放了哪些接口能力,如何接入审批能力以及审批案例等内容。

前提条件

已在Dataphin系统配置审批设置,如何配置,详情请参见审批设置。运维租户参见审批设置选择审批系统其他配置项。

审批接口

重要

{url} :为第三方审批流对接地址,http://{url}为提交审批申请URL,协议仅支持HTTP,暂不支持HTTPS。

  1. 连接测试。

    请求方式:POST。

    请求地址:http://{url}/dataphin/bpms/check/connect。

    请求时序图

    image

    Query参数

    参数名称

    参数类型

    描述

    accessToken

    String

    调用服务端API的凭证。

    Dataphin页面自动生成,或用户生成填入Dataphin。如:6d1b2n1c。

    timestamp

    String

    单位毫秒,信息发送时间戳。如:1654156836531

    Body参数

    参数名称

    参数类型

    描述

    checkEvent

    String

    检查事件为连接测试。如:connectivity。

    返回参数

    参数名称

    参数类型

    描述

    checkResult

    String

    检查结果:成功,仅当返回值为success时,认定为成功。其他值均被认为测试连接失败。如:success。

  2. 提交审批。

    请求方式:POST。

    请求地址:http://{url}/dataphin/bpms/processinstance/create。

    提交审批的时序图

    image

    Query参数

    参数名称

    参数类型

    描述

    accessToken

    String

    调用服务端API的凭证。

    Dataphin页面自动生成,或用户生成填入Dataphin。如:6d1b2n1c。

    timestamp

    String

    单位毫秒,信息发送时间戳。如:1654156836531

    Body参数

    参数名称

    参数类型

    描述

    applyId

    String

    Dataphin审批单Id。如:1223。

    title

    String

    Dataphin审批单标题。如:dataphin bpms。

    content

    String

    Dataphin审批单内容。如:bpms content。请参见审批消息内容元数据描述

    type

    String

    • 审批单类型:APPROVAL_ DOC_TYPE。

    • 代码审核:CODE_REVIEW。

    • 发布管控:PUBLISH。

    • 业务规划:BIZ_PLANNING。

    • 权限审批:AUTH。

    • 默认:DEFAULT。

    templateCode

    String

    审批模板code,可为空,与页面配置有关。

    审批消息内容元数据描述

    参数名称

    参数类型

    描述

    resourceType

    String

    审批任务类型,为枚举值,包含以下的类型:

    • PhysicalTable:物理表。

    • LogicTable:逻辑表。

    • MetaTable:实时元表。

    • MirrorTable:镜像表。

    • Fun:函数。

    • DataSource:数据源。

    • FeatureConfig:功能权限配置。

    • OSAPP:数据服务APP。

    • OSAPI:数据服务API 。

    • OSLogicUnit:数据服务单元。

    • OSDS:数据服务数据源。

    • SECRET_KEY:密钥。

    • GLOBAL_PARAM:全局变量。

    grantToUsers

    List<GrantToUser>

    授权用户列表。请参见GrantToUser

    bpmsEnvironment

    BpmsEnvironment

    审批系统环境信息。请参见BpmsEnvironment

    operates

    List<String>

    申请的权限类型,包含以下类型:

    • SYNC_READ:数据源同步读。

    • SYNC_WRITE:数据源同步写。

    • SQL_QUERY: 物理表及逻辑表的SQL查询。

    • SQL_WRITE: 物理表的SQL写入。

    • SQL_ALTER: 物理表的SQL修改。

    • SQL_DROP: 物理表及逻辑表的SQL删除。

    • SELECT:数据服务API的查询。

    • WRITE:写入权限。

    • DEV:开发权限。

    • USE:使用权限。

    • UPDATE:改表数据。

    • PIPELINE_ENCRY:集成加密。

    • PIPELINE_DECRY:集成解密。

    levels

    List<String>

    权限等级,包含三个等级:HIGH、MIDDLE、LOW。

    operations

    List<String>

    权限类型,包含以下类型:

    • SELECT-查询。

    • DESCRIBE- 查表结构。

    • UPDATE- 改表数据。

    • ALTER- 改表结构。

    • DELETE-删除表。

    • COPY_TASK- 复制。

    resources

    List<BpmsResource>

    资源内容,请参见BpmsResource

    applyObject

    ApplyObject

    申请对象信息,请参见ApplyObject

    reason

    String

    申请原因。

    GrantToUser

    参数名称

    参数类型

    描述

    account

    Account

    授权账户列表,请参见Account

    period

    Period

    有效期,请参见Period

    Account

    参数名称

    参数类型

    描述

    accountType

    String

    授权类型,包括个人账号生产账号应用账号三种。

    • PERSONAL:个人账号。

    • PRODUCE:生产账号。

    • APPLICATION:应用账号。

    userName

    String

    结束时间格式为yyyy-mm-dd。如:2022-09-11。

    Period

    参数名称

    参数类型

    描述

    periodType

    String

    有效期类型支持长期(LONG_TERM

    periodEnd

    String

    权限到期时间格式为yyyy-mm-dd。如:2022-09-11。

    BpmsEnvironment

    参数名称

    参数类型

    描述

    projectName

    String

    项目名称。

    bizUnitName

    String

    业务板块名称。

    resourceEnv

    String

    环境分为生产环境和开发环境两种环境。

    • PROD:生产。

    • DEV:开发。

    BpmsResource

    参数名称

    参数类型

    描述

    resourceType

    String

    请参见审批消息内容元数据描述

    resourceName

    String

    资源名称。

    children

    List<Children>

    字段列表,请参见Children

    operations

    List<String>

    请参见审批消息内容元数据描述的operates。

    authTypes

    String

    申请权限类型。

    Children

    参数名称

    参数类型

    描述

    resourceName

    String

    字段名称。

    resourceProperties

    String

    字段属性。

    ResourceProperties

    参数名称

    参数类型

    描述

    columnType

    String

    字段类型。

    columnIsPartition

    String

    是否为分区字段。

    columnIsPk

    String

    是否为主键。

    ApplyObject

    参数名称

    参数类型

    描述

    objectName

    String

    对象名称。

    codeContent

    String

    代码内容。

    name

    String

    业务活动英文名称。

    bizObjectType

    String

    对象类型。

    bizObjectChangeType

    String

    变更类型。

    bizObjectPkField

    String

    组件字段。

    bizObjectParent

    String

    所属父对象。

    bizObjectChildren

    String

    下游子对象。

    bizProcessCn

    String

    活动名称。

    bizProcessType

    String

    活动类型。

    bizProcessChangeType

    String

    变更类型。

    bizProcessNodes

    String

    流程节点。

    atomicIndexCn

    String

    指标名称。

    dataType

    String

    数据类型。

    unit

    String

    计量单位。

    bizProcess

    String

    活动名称。

    des

    String

    活动口径。

    derivedLogic

    String

    衍生公司。

    protoLogics

    String

    计算逻辑。

    返回参数

    参数名称

    参数类型

    描述

    processInstanceId

    String

    第三方审批实例Id。如:6d1b2n1c。

  3. 撤销审批。

    请求方式:POST。

    请求地址:http://{url}/dataphin/bpms/processinstance/revoke。

    Query参数

    参数名称

    参数类型

    描述

    accessToken

    String

    调用服务端API的凭证。

    Dataphin页面自动生成,或用户生成填入Dataphin。如:6d1b2n1c。

    timestamp

    String

    单位毫秒,信息发送时间戳。如:1654156836531

    Body参数

    参数名称

    参数类型

    描述

    applyId

    String

    Dataphin审批单Id。如:1223。

    processInstanceId

    String

    第三方审批实例Id。如:6d1b2a3c。

    operatingUserId

    String

    操作人工号。如:6d1bx1d2。

    返回参数

    参数名称

    参数类型

    描述

    result

    String

    撤回结果信息。仅当返回值为success时被认定为成功。

  4. 查询审批实例Url。

    请求方式:GET。

    请求地址:http://{url}/dataphin/bpms/processinstance/apply。

    Query参数

    参数名称

    参数类型

    描述

    accessToken

    String

    调用服务端API的凭证。

    Dataphin页面自动生成,或用户生成填入Dataphin。如:6d1b2n1c。

    timestamp

    String

    单位毫秒,信息发送时间戳。如:1654156836531

  5. 返回参数。

回调接口

重要
  • https://{callbackUrl}即为审批设置其他配置的Callback URL。

  • accessToken:调用服务端API的凭证。Dataphin页面自动生成,或用户生成填入Dataphin。

  • callBackAesKey:回调认证凭证。Dataphin页面自动生成,或用户生成填入Dataphin。

  1. 回调方法。

    请求方式:POST。

    请求地址:http://{callbackUrl}。

    Query参数

    参数名称

    参数类型

    描述

    signature

    String

    消息认证信息,请参见加解密方法。

    timestamp

    String

    单位毫秒,信息发送时间戳。如:1654156836531。

    nonce

    String

    随机数,请参见加解密方法。

    Body参数

    参数名称

    参数类型

    描述

    encrypt

    String

    加密信息。如:ajls384k。

    返回参数

    参数名称

    参数类型

    描述

    encrypt

    String

    回调状态加密信息。如:ajls384k。

  2. 回调加密内容。

    回调连通性校验。

    • 传入加密参数:

      参数名称

      参数类型

      描述

      applyStatus

      String

      检查连通性参数。如:CHECK。

    • 返回加密参数:

      返回值

      描述

      success

      成功回调返回值。

      回调返回bpms实例审批结果

    • 传入加密参数:

      参数名称

      参数类型

      描述

      applyId

      String

      Dataphin审批单Id。如:1223。

      processInstanceId

      String

      第三方审批实例Id。如:6d1b2a3c。

      comment

      String

      审批实例评论信息。

      applyStatus

      String

      • accept:接受。

      • reject:拒绝。

      • revoke:撤销。

    • 返回加密参数:

      返回值

      描述

      success

      成功回调返回值。

  3. 加解密方法。

    加密示例:

    Map<String, String> callBackJson = Maps.newHashMap();
    callBackJson.put("applyId", "1");
    callBackJson.put("applyStatus", "accept");
    callBackJson.put("processInstanceId", "2");
    callBackJson.put("comment", "test");
    String callBackResult = JSON.toJSONString(callBackJson);
    
    ThirdPartyCrypto callbackCrypto = new ThirdPartyCrypto(token, aesKey);
    
    String timestamp = String.valueOf(System.currentTimeMillis());
    String nonce = ThirdPartyCrypto.Utils.getRandomStr(16);
    String encrypt = callbackCrypto.encrypt(nonce, callBackResult);
    String signature = callbackCrypto.getSignature(token, timestamp, nonce, encrypt);

    解密示例:

    String encryptMsg = JSONObject.parseObject(encrypt);
    String decryptMsg = callbackCrypto.getDecryptMsg(signature, timestamp, nonce, encryptMsg);
    if ("success".equalsIgnoreCase(decryptMsg)) {
     log.error("call back success");
    }

    加解密工具

    import com.alibaba.fastjson.JSON;
    import org.apache.commons.codec.binary.Base64;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.ByteArrayOutputStream;
    import java.lang.reflect.Field;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.security.Permission;
    import java.security.PermissionCollection;
    import java.security.Security;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Random;
    
    /**
     * Dataphin开放平台加解密方法
     */
    public class ThirdPartyCrypto {
    
     private static final Charset CHARSET = StandardCharsets.UTF_8;
     private static final Base64 BASE_64 = new Base64();
     private final byte[] AES_KEY;
     private final String TOKEN;
    
     /**
     * ask getPaddingBytes key固定长度
     **/
     private static final Integer AES_ENCODE_KEY_LENGTH = 43;
     /**
     * 加密随机字符串字节长度
     **/
     private static final Integer RANDOM_LENGTH = 16;
    
     /**
     * 构造函数
     *
     * @param token 开发者设置的token
     * @param encodingAesKey 开发者设置的EncodingAESKey
     *
     * @throws ThirdPartyEncryptException 执行失败,请查看该异常的错误码和具体的错误信息
     */
     public ThirdPartyCrypto(String token, String encodingAesKey) throws ThirdPartyEncryptException {
     if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.AES_KEY_ILLEGAL);
     }
     this.TOKEN = token;
     AES_KEY = Base64.decodeBase64(encodingAesKey + "=");
     }
    
     public Map<String, String> getEncryptedMap(String plaintext) throws ThirdPartyEncryptException {
     return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));
     }
    
     /**
     * 将和Dataphin同步的消息体加密,返回加密Map
     *
     * @param plaintext 传递的消息体明文
     * @param timeStamp 时间戳
     * @param nonce 随机字符串
     * @return
     * @throws ThirdPartyEncryptException
     */
     public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce)
     throws ThirdPartyEncryptException {
     if (null == plaintext) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
     }
     if (null == timeStamp) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
     }
     if (null == nonce) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_NONCE_ILLEGAL);
     }
     // 加密
     String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
     String signature = getSignature(TOKEN, String.valueOf(timeStamp), nonce, encrypt);
     Map<String, String> resultMap = new HashMap<String, String>();
     resultMap.put("msg_signature", signature);
     resultMap.put("encrypt", encrypt);
     resultMap.put("timeStamp", String.valueOf(timeStamp));
     resultMap.put("nonce", nonce);
     return resultMap;
     }
    
     /**
     * 密文解密
     *
     * @param msgSignature 签名串
     * @param timeStamp 时间戳
     * @param nonce 随机串
     * @param encryptMsg 密文
     * @return 解密后的原文
     * @throws ThirdPartyEncryptException
     */
     public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)
     throws ThirdPartyEncryptException {
     //校验签名
     String signature = getSignature(TOKEN, timeStamp, nonce, encryptMsg);
     if (!signature.equals(msgSignature)) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_SIGNATURE_ERROR);
     }
     // 解密
     return decrypt(encryptMsg);
     }
    
     /**
     * 对明文加密.
     * @param plaintext 需要加密的明文
     * @return 加密后base64编码的字符串
     */
     public String encrypt(String random, String plaintext) throws ThirdPartyEncryptException {
     try {
     byte[] randomBytes = random.getBytes(CHARSET);
     byte[] plainTextBytes = plaintext.getBytes(CHARSET);
     byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
     ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
     byteStream.write(randomBytes);
     byteStream.write(lengthByte);
     byteStream.write(plainTextBytes);
     byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
     byteStream.write(padBytes);
     byte[] unencrypted = byteStream.toByteArray();
     byteStream.close();
     Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
     SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES");
     IvParameterSpec iv = new IvParameterSpec(AES_KEY, 0, 16);
     cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
     byte[] encrypted = cipher.doFinal(unencrypted);
     String result = BASE_64.encodeToString(encrypted);
     return result;
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
     }
     }
    
     /**
     * 对密文进行解密.
     * @param text 需要解密的密文
     * @return 解密得到的明文
     */
     private String decrypt(String text) throws ThirdPartyEncryptException {
     byte[] originalArr;
     try {
     // 设置解密模式为AES的CBC模式
     Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
     SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES");
     IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(AES_KEY, 0, 16));
     cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
     // 使用BASE64对密文进行解码
     byte[] encrypted = Base64.decodeBase64(text);
     // 解密
     originalArr = cipher.doFinal(encrypted);
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
     }
    
     String plainText;
     try {
     // 去除补位字符
     byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
     // 分离16位随机字符串,网络字节序和corpId
     byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
     int plainTextLegth = Utils.bytes2int(networkOrder);
     plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
     }
    
     return plainText;
     }
    
     /**
     * 数字签名
     *
     * @param token isv token
     * @param timestamp 时间戳
     * @param nonce 随机串
     * @param encrypt 加密文本
     * @return
     * @throws ThirdPartyEncryptException
     */
     public String getSignature(String token, String timestamp, String nonce, String encrypt)
     throws ThirdPartyEncryptException {
     try {
     String[] array = new String[] {token, timestamp, nonce, encrypt};
     Arrays.sort(array);
     System.out.println(JSON.toJSONString(array));
     StringBuffer sb = new StringBuffer();
     for (int i = 0; i < 4; i++) {
     sb.append(array[i]);
     }
     String str = sb.toString();
     System.out.println(str);
     MessageDigest md = MessageDigest.getInstance("SHA-1");
     md.update(str.getBytes());
     byte[] digest = md.digest();
    
     StringBuffer hexstr = new StringBuffer();
     String shaHex = "";
     for (int i = 0; i < digest.length; i++) {
     shaHex = Integer.toHexString(digest[i] & 0xFF);
     if (shaHex.length() < 2) {
     hexstr.append(0);
     }
     hexstr.append(shaHex);
     }
     return hexstr.toString();
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_SIGNATURE_ERROR);
     }
     }
    
     public static class Utils {
     public Utils() {
     }
    
     public static String getRandomStr(int count) {
     String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
     Random random = new Random();
     StringBuffer sb = new StringBuffer();
    
     for (int i = 0; i < count; ++i) {
     int number = random.nextInt(base.length());
     sb.append(base.charAt(number));
     }
    
     return sb.toString();
     }
    
     public static byte[] int2Bytes(int count) {
     byte[] byteArr = new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),
     (byte)(count & 255)};
     return byteArr;
     }
    
     public static int bytes2int(byte[] byteArr) {
     int count = 0;
    
     for (int i = 0; i < 4; ++i) {
     count <<= 8;
     count |= byteArr[i] & 255;
     }
    
     return count;
     }
     }
    
     public static class PKCS7Padding {
     private static final Charset CHARSET = StandardCharsets.UTF_8;
     private static final int BLOCK_SIZE = 32;
    
     public PKCS7Padding() {
     }
    
     public static byte[] getPaddingBytes(int count) {
     int amountToPad = 32 - count % 32;
     if (amountToPad == 0) {
     amountToPad = 32;
     }
    
     char padChr = chr(amountToPad);
     String tmp = new String();
    
     for (int index = 0; index < amountToPad; ++index) {
     tmp = tmp + padChr;
     }
    
     return tmp.getBytes(CHARSET);
     }
    
     public static byte[] removePaddingBytes(byte[] decrypted) {
     int pad = decrypted[decrypted.length - 1];
     if (pad < 1 || pad > 32) {
     pad = 0;
     }
    
     return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
     }
    
     private static char chr(int a) {
     byte target = (byte)(a & 255);
     return (char)target;
     }
     }
    
     public static class ThirdPartyEncryptException extends Exception {
     public static final int SUCCESS = 0;
     public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
     public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
     public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;
     public static final int AES_KEY_ILLEGAL = 900004;
     public static final int SIGNATURE_NOT_MATCH = 900005;
     public static final int COMPUTE_SIGNATURE_ERROR = 900006;
     public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
     public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
     public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
     public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
     private static Map<Integer, String> msgMap = new HashMap();
     private Integer code;
    
     static {
     msgMap.put(0, "成功");
     msgMap.put(900001, "加密明文文本非法");
     msgMap.put(900002, "加密时间戳参数非法");
     msgMap.put(900003, "加密随机字符串参数非法");
     msgMap.put(900005, "签名不匹配");
     msgMap.put(900006, "签名计算失败");
     msgMap.put(900004, "不合法的aes key");
     msgMap.put(900007, "计算加密文字错误");
     msgMap.put(900008, "计算解密文字错误");
     msgMap.put(900009, "计算解密文字长度不匹配");
     msgMap.put(900010, "计算解密文字corpid不匹配");
     }
    
     public Integer getCode() {
     return this.code;
     }
    
     public ThirdPartyEncryptException(Integer exceptionCode) {
     super((String)msgMap.get(exceptionCode));
     this.code = exceptionCode;
     }
     }
     static {
     try {
     Security.setProperty("crypto.policy", "limited");
     RemoveCryptographyRestrictions();
     } catch (Exception var1) {
     }
    
     }
     private static void RemoveCryptographyRestrictions() throws Exception {
     Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity");
     Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");
     Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");
     if (jceSecurity != null) {
     setFinalStaticValue(jceSecurity, "isRestricted", false);
     PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class);
     if (cryptoPermissions != null) {
     Map<?, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);
     map.clear();
     }
    
     if (cryptoAllPermission != null) {
     Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class);
     defaultPolicy.add(permission);
     }
     }
    
     }
     private static Class<?> getClazz(String className) {
     Class clazz = null;
    
     try {
     clazz = Class.forName(className);
     } catch (Exception var3) {
     }
    
     return clazz;
     }
     private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception {
     Field field = srcClazz.getDeclaredField(fieldName);
     field.setAccessible(true);
     Field modifiersField = Field.class.getDeclaredField("modifiers");
     modifiersField.setAccessible(true);
     modifiersField.setInt(field, field.getModifiers() & -17);
     field.set((Object)null, newValue);
     }
     private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception {
     Field field = srcClazz.getDeclaredField(fieldName);
     field.setAccessible(true);
     return dstClazz.cast(field.get(owner));
     }
    
    }