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

Posted JAVA炭烧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021,你还在写“赤裸裸”的API吗?相关的知识,希望对你有一定的参考价值。


前言

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

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

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

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时知道怎样去保证接口的安全性,希望对大家有所帮助。

前段时间,在和群友聊天时,把今年他们见到的一些不同类别的面试题整理了一番,于是有了以下面试题集,也一起分享给大家~

如果你觉得这些内容对你有帮助,可以加入csdn进阶交流群,领取资料

基础篇


JVM 篇


mysql



Redis 篇




由于篇幅限制,详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

如果你觉得这些内容对你有帮助,可以加入csdn进阶交流群,领取资料

以上是关于2021,你还在写“赤裸裸”的API吗?的主要内容,如果未能解决你的问题,请参考以下文章

《你还在写sql语句吗?》人生苦短,进入MybatisPlus的丝滑体验

都2021年了,你还在写ES5 ?快醒醒吧,这份ES6超全教程拯救你

都2021年了,你还在写ES5 ?快醒醒吧,这份ES6超全教程拯救你

struts2配置通用action一个顶N个,你还在写一大堆action吗?

你还在苦逼地findViewById吗?使用ButterKnife从此轻松定义控件

你还在写垃圾代码?快用 Java 8 重构传统设计模式吧,是真的优雅!