5

2021,你还在写“赤裸裸”的API吗?

 3 years ago
source link: https://my.oschina.net/u/5079097/blog/5135528
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

新的项目要开始了,由于项目中涉及到和第三方厂商的接口对接,所有对接口的安全性有要求,下来我们看看如何写好一个规范的接口

如何保证接口传输中数据安全

保证数据安全,大家首先能想到的就是对内容进行加密。加密的话主要有两种方式,对称加密和非对称加密。

1、对称加密

我们先一起看一张图

image.png 可以看到,上图中采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。对称加密有很多种算法,由于它效率很高,所以被广泛使用在很多加密协议的核心当中。对称加密的强度取决于密钥的大小,密钥也大,越难破解,但同时加密和解密的过程中耗时也更长。常用的对称加密算法有DES、3DES、TDEA、Blowfish、RC2、RC4、RC5、IDEA、SKIPJACK等。下面我们看下使用DES进行对称加密的示例。 首先是加密部分

/**
     * 加密
     * @param data 待加密内容
     * @param secretKey 密钥
     * @return
     */
    public static byte[] encrypt(byte[] data, String secretKey) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(secretKey.getBytes());
            // 创建一个密匙工厂,然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
            cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
            // 现在,获取数据并加密
            return cipher.doFinal(data);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
复制代码

然后是解密部分

/**
     * 解密
     * @param src
     * @param secretKey
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] src, String secretKey) throws Exception {
        // DES算法要求有一个可信任的随机数源
        SecureRandom random = new SecureRandom();
        // 创建一个DESKeySpec对象
        DESKeySpec desKey = new DESKeySpec(secretKey.getBytes());
        // 创建一个密匙工厂,返回实现指定转换的
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        // 将DESKeySpec对象转换成SecretKey对象
        SecretKey secureKey1 = keyFactory.generateSecret(desKey);
        // 获取Cipher对象
        Cipher cipher = Cipher.getInstance("DES");
        // 初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, secureKey1, random);
        // 解密
        return cipher.doFinal(src);
    }
复制代码

接下来我们测试下

    public static void main(String[] args) throws Exception {
        // 待加密内容
        String str = "https://juejin.cn/user/2084329778071479";
        // 密钥
        String secretKey = "xtianyaa";
        byte[] encrypt = encrypt(str.getBytes(), secretKey);
        System.out.println("加密前:" +str);
        System.out.println("加密后:" + new String(encrypt));
        // 解密
        byte[] decrypt = decrypt(encrypt, secretKey);
        System.out.println("解密后:" + new String(decrypt));
    }
复制代码
加密前:https://juejin.cn/user/2084329778071479
加密后:SA(��j�tj��G΃/�[M(�  �y�݇t.�7z"D�3 �<
解密后:https://juejin.cn/user/2084329778071479
复制代码

2、非对称加密

非对称加密算法需要两个密钥:公钥私钥。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将公钥公开,需要向甲方发送信息的其他角色(乙方)使用该密钥(甲方的公钥)对机密信息进行加密后再发送给甲方;甲方再用自己私钥对加密后的信息进行解密。甲方想要回复乙方时正好相反,使用乙方的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密。我们看下非对称加密和对称加密的区别 image.png

非对称密码体制的特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就大了很多。

关于非对称加密的实现算法主要有:RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法),使用最广泛的是RSA算法。

如何证明你是你

关于API接口另一个重要的部分就是签名,通过签名,才知道接口的调用方有没有被进行篡改。下面一起看下接口签名的实现

    /**
     *
     * @param appId  调用方的ID,唯一
     * @param appKey 调用方APP_KEY,每个调用方固定一个字符串,唯一
     * @param sign   签名,appId+appKey + request秘钥(不参与接口参数传递,本地保存) + 时间戳(以long类型的字符串.格式:yyyyMMddHHmmss),经过MD5加密后生成的串(字母小写)
     * @param requestTime 时间戳(以long类型的字符串)
     * @param type  数据类型
     * @param data  数据内容  使用非对称加密,接口调用者用公钥加密,接收后用私钥进行解密
     * @return
     */
    @PostMapping("/system_api")
    @Limit(key = "get_system_api", period = 60, count = 10, name = "主屏接口数据采集", prefix = "limit")
    public String systemApi(@RequestHeader("APP_ID") String appId,
                               @RequestHeader("APP_KEY") String appKey,
                               @RequestHeader("TOKEN") String sign,
                               @RequestHeader("TIMESTAMP") String requestTime,
                               @RequestParam String type, @RequestBody String data){
        
        String checkInfo = paramsCheck(appId,appKey,sign,requestTime);
        if(!SUCCESS.equals(checkInfo)){
            return checkInfo;
        }
        return SUCCESS;
    }
复制代码

可以看到,其中sign通过appId+appKey + request秘钥+ 时间戳,经过MD5加密后生成,当我们拿到这个sign时,再根据对应的参数进行验证

**
     * 根据接口请求的参数进行身份验证
     * @param appId
     * @param appKey
     * @param sign
     * @param requestTime
     */
    private String paramsCheck(String appId, String appKey, String sign, String requestTime) {
        AppConfig appConfig =  appConfigService.getAppConfig(appId);
        String errorInfo = SUCCESS;
        //1、判断key是否正确
        if (!appKey.equals(appConfig.getAppKey())){
            errorInfo = "500";
        }
        //2、拼接字符串
        String key = appId+appConfig.getAppKey()+appConfig.getAppSecret()+requestTime;
        //3、根据appId+appKey+AppSecret+requestTime,经过md5后, 生成签名
        String newSign = DigestUtils.md5DigestAsHex(key.getBytes());
        //4、判断签名是否经过篡改
        if(!sign.equals(newSign)){
            errorInfo = "500";
        }
        //5、判断请求时间是否异常,防止使用过期签名进行身份认证
        Date date = null;
        try {
             date = new SimpleDateFormat("yyyyMMddHHmmss").parse(requestTime);
        } catch (ParseException e) {
            log.error(e.getMessage());
        }
        long second = 0;
        if (date != null) {
            second = getDatePoor(new Date(),date);
        }else{
            errorInfo = "TIME IS NULL";
        }
        if(second>TIME_OUT){
            errorInfo = "TIME OUT";
        }
        return errorInfo;
    }
复制代码

可以看到我们在生成sign时增加了时间戳字段,主要就是防止他们利用获取到的sign直接进行使用,而一旦加了时间戳的话,就可以进行过期处理。

好了,看完这里相信大家在写OPEN API时知道怎样去保证接口的安全性,希望对大家有所帮助。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK