# 2.1 使用秘钥授权登录

# 2.1.1 获取接入AccessToken

接口说明:用于第三方系统从外部直接登录勤策系统时获取AccessToken信息,此AccessToken仅用于秘钥授权接入使用和oAuth2接口不可混用。

请求方式: POST(HTTPS
请求地址: https://{region}/openplat/getTokenFromThirdparty.do

参数说明:

参数 类型 是否必传 说明
region text 企业所在数据中心服务地址,如:数据中心1区地址:https://cloud.region1.qince.com
tenantId long 企业id
data text 加密数据体,将数据体原文使用企业参数"OA登录秘钥"进行AES加密DataEncryptUtil.encryptText(DATA, OA_KEY, NONCE, TIMESTAMP),加密方法参考 2.1.4 加密方法部分
nonce text 随机字符串
timestamp long 时间戳

请求体加密后示例:

{
	"tenantId":4802948302940558496,
    "data":"P3EENkBmTTJ2OxZxl+/BTWED4D/GZNQPL6Q6pNqmDkcsCCa/ia/R5mQwWdDsQTa7PF5IguXVNwhrEnotDaT5/B8c/XLHiS9sUPfFRp5vfyWwPKbjzVLJ+j+3SfypAQiI5S8nDhUUHVG1Qy/wYV9MvhtlI0/kgXLsLr8NbFedyPdZzlKEtPZAZCawQqq1Llu0",
	"nonce":"1234",
	"timestamp":20220708142900
}

data原文格式说明:

参数 类型 是否必传 说明
sourceType text 登录方式 WEB-电脑端 CLIENT-手机APP端
redirectUrl text 登录成功后跳转地址,需要使用勤策URL链接相对地址
tenantId long 企业ID
userId long 勤策员工唯一ID和thirdId二者必传其中一个
thirdId text 第三方员工唯一ID和userId二者必传其中一个

data原文示例:

{
    "sourceType": "WEB",
    "redirectUrl": "/test.html",
    "tenantId": 4802948302940558496,
    "thirdId": "123456"
}

响应参数

参数 类型 说明
code text 响应码 1-成功 0-失败
access_token text SSO请求访问令牌
expire_in long SSO请求访问令牌过期时间有效期,单位秒。 默认86400秒
message text 响应信息

响应示例:

{
    "code": 1,
    "data": {
        "access_token": "qc4802948302940558496ak5XLynGNh3e7a04a1b6d54fd8bf0451db8958c823",
        "expire_in": 86400
    },
    "message": "ok"
}

# 2.1.2 用AccessToken登录跳转到勤策WEB端

接口说明:用于第三方系统直接使用AccessToken从外部打开勤策系统的功能界面的接口。

请求方式: GET(HTTPS
请求地址: https://{region}/openplat/redirectFromThirdparty.do?accessToken=<accessToken>

请求体示例:

    https://cloud.region1.qince.com/openplat/redirectFromThirdparty.do?accessToken=qc4802948302940558496ak5XLynGNh3e7a04a1b6d54fd8bf0451db8958c823

参数说明:

参数 类型 是否必传 说明
region text 企业所在数据中心服务地址,如:数据中心一区地址:https://cloud.region1.qince.com
accessToken text 请求跳转令牌 2.1.1获取AccessToken

# 2.1.3 用AccessToken登录勤策APP端

接口说明:用于第三方APP可以使用URL Schemes唤起勤策APP,第三方APP在手机端直接唤起勤策APP时需要携带AccessToken参数以便单点登录到勤策系统主界面。

# 2.1.3.1 安卓客户端唤起

请求地址: <Schema>://<HOST>?access_token=<accessToken>

参数 类型 是否必传 说明
Schema text 应用Schema,由勤策厂商提供
HOST text 应用HOST,由勤策厂商提供
accessToken text 登录授权AccessToken信息, 2.1.1获取AccessToken

示例:

qince://qince?access_token=qc4802948302940558496ak5XLynGNh3e7a04a1b6d54fd8bf0451db8958c823

# 2.1.3.2 苹果客户端唤起

请求地址: <Schema>://access_token=<accessToken>

参数 类型 是否必传 说明
Schema text 应用Schema,由勤策厂商提供
accessToken text 登录授权AccessToken信息,2.1.1获取AccessToken

示例:

qince://access_token=qc4802948302940558496ak5XLynGNh3e7a04a1b6d54fd8bf0451db8958c823

# 2.1.4 生成加密数据方法

接口说明:用于第三方系统获取的AccessToken信息时获取加密数据的方法。需要传递4个参数text为加密文本信息,nonce随机字符串,

参数 类型 是否必传 说明
text text 待加密的文本信息
oaKey text 企业OA登录秘钥,在企业参数中设置
nonce text 随机字符串
timestamp long 时间戳

加密算法:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;

public class DataEncryptUtil
{
    private static final int KEY_SIZE = 256;

    /** 加密/解密算法名称 */
    private static final String ALGORITHM = "AES";
    //"算法/模式/补码方式"
    private static final String TRANSFORM_CBC_PKCS5 = "AES/ECB/PKCS5Padding";

    private static final String CHARSET = "UTF-8";


    private static byte[] decrypt(byte[] cipherBytes, byte[] key) throws Exception
    {
        // 生成密钥对象
        SecretKey secKey = generateKey(key);

        // 获取 AES 密码器
        Cipher cipher = Cipher.getInstance(TRANSFORM_CBC_PKCS5);
        // 初始化密码器(解密模型)
        cipher.init(Cipher.DECRYPT_MODE, secKey);

        // 解密数据, 返回明文
        byte[] plainBytes = cipher.doFinal(cipherBytes);

        return plainBytes;
    }

    public static String decrypt(String decryptText, String key) throws Exception
    {
        byte[] plainBytes = decrypt(Base64.getDecoder().decode(decryptText.getBytes(CHARSET)), key.getBytes(CHARSET));
        return new String(plainBytes, CHARSET);
    }

    /**
     * 生成密钥对象
     */
    private static SecretKey generateKey(byte[] key) throws Exception
    {
        return new SecretKeySpec(key, ALGORITHM);
    }

    private static byte[] encrypt(byte[] plainBytes, byte[] key) throws Exception
    {
        // 生成密钥对象
        SecretKey secKey = generateKey(key);

        // 获取 AES 密码器
        Cipher cipher = Cipher.getInstance(TRANSFORM_CBC_PKCS5);
        // 初始化密码器(加密模型)
        cipher.init(Cipher.ENCRYPT_MODE, secKey);

        // 加密数据, 返回密文
        byte[] cipherBytes = cipher.doFinal(plainBytes);

        return cipherBytes;
    }

    public static String encrypt(String text, String key) throws Exception
    {
        byte[] cipherBytes = encrypt(text.getBytes(CHARSET), key.getBytes(CHARSET));

        String base64encodedString = Base64.getEncoder().encodeToString(cipherBytes);
        return base64encodedString;
    }

    private static synchronized String md5(String data)
    {
        MessageDigest digest = null;
        if(digest == null)
        {
            try
            {
                digest = MessageDigest.getInstance("MD5");
                digest.update(data.getBytes(CHARSET));
            }
            catch(Exception nsae)
            {
                throw new RuntimeException("Failed to load the MD5 MessageDigest.");
            }
        }
        return byteToHex(digest.digest());
    }

    private static String byteToHex(byte[] hash)
    {
        StringBuffer buf = new StringBuffer(hash.length * 2);
        int i;
        for(i = 0; i < hash.length; i++)
        {
            if((hash[i] & 0xff) < 0x10)
            {
                buf.append("0");
            }
            buf.append(Long.toString(hash[i] & 0xff, 16));
        }
        return buf.toString();
    }

    public static String encryptText(String text, String oaKey, String nonce , long timestamp) throws Exception
    {
        String key = String.format("%s|%s|%s", oaKey, nonce, timestamp);
        return encrypt(text, md5(key));
    }

    public static String decryptText(String text, String oaKey, String nonce , long timestamp) throws Exception
    {
        String key = String.format("%s|%s|%s", oaKey, nonce, timestamp);
        return decrypt(text, md5(key));
    }


    public static void main(String[] args) throws Exception
    {
        String e = encryptText("aaaa", "xyr","1234", 12345667);
        System.out.println("Base64 编码字符串 (加密) :" + e);
        String d = decryptText(e, "xyr","1234", 12345667);
        System.out.println("Base64 编码字符串 (原文) :" + d);
    }
}