PHP安装并运行

由于用JAVA开发的后台接口在和前端PHP人员调试时一直报错,怎么改前端都说都不行,没办法,只好自己动手了.

问题场景

  • 某SpringBoot项目定义了一系列的接口,供前端调用,由于网络传输,所以返回时需要加密,这边使用的是AES对对象实体进行加密.
  • 前端PHP人员取得了加密数据,并且解密成功了,肉眼观察没有任何问题.
  • 然后就出现问题了,解密后的字符串需要转成JSON格式才好处理,我这边也是将对象转成JSON字符串回传的,但前端解析JSON时一直报错,一直报错,一直报错.
  • 前端手动复制解密后的字符串进行转JSON是可以的.
    前端那边一直解决不了,于是我们这边进行问题复现,自己写 java 代码调用接口,发现返回的加密字符串前后各多了一个双引号,于是就以为问题找到了.
  • 接下来就是解决问题, controller 层类的方法使用了 @ResponseBody 注解,会自动将返回对象转为 JSON 字符串,由于我们需要加密,于是就在返回对象前,将对象提前转为 JSON 字符串并加密.
  • @ResponseBody 对于实体对象会自动转为 JSON 字符串,对于已经是字符串的,会在字符串两端添加上双引号.
  • 所以我们要么将接口方法上的 @ResponseBody 注解去掉,直接返回 String 字符串,要么就将加密字符串放到一个对象里面去,最终我们采取了第二种方法.
  • JAVA 再次测试时,确实可以了,然后通知前端PHP人员没有问题了,可是结果还是有问题,还是转 JSON 转不了.他们发现解密后的字符串里面多了一些特殊占位符,去掉之后就可以转 JSON 了.于是问题就这样解决了,代码上线了.
  • 但是我这边有点上头了,感觉需要找出这些特殊占位符是哪来的,不可能凭空产生, JAVA 测试代码没有问题,于是我就打算用 PHP 再写一段测试代码,来调用接口,看看到底哪里出了问题.

PHP安装

  • 网上饶了一圈,发现官网的安装教程就是个坑,然后其他地方的安装教程也很复杂.
  • PHP 安装很简单,下载一个 wampserver64 就可以了.
  • 然后开发工具使用的是 PhpStorm,官网打开会有点卡.
  • 安装好 wampserver 和 PhpStorm 后,打开 PhpStorm ,配置一下 CLI,指向 wampserver 安装包下的 php.exe 文件,就安装好了.
    在这里插入图片描述

PHP代码编写

这边的代码其实大部分都是网上百度的,因为很简单,只涉及发送 post 请求, AES解密, 并解析数据.

<?php
include("Aes.php");

//使用方法
$post_data = array(
    'username' => 'a',
    'password' => 'a'
);

// 查询接口
$result = send_post('url', $post_data);
// 返回结果
$result2 = json_decode($result) ->result;

// 解密
// 使用AES解密
$aes = new Aes();
$decode = $aes ->decrypt($result2);

// 打印解密后的字符串的ASC码
for ($i=0; $i < $strlen ; $i++) {
    echo $decode[$i]. '   '. ord($decode[$i]);
    echo "<br>";
}

// 打印后发现最后多了几个ASC码为0的空值

// 清除空值
//$decode = trim($decode);

// 将 String 转换成 JSON
//$data = json_decode($decode);

// 从 JSON 字符串里面取值
//$code = $data ->data;

// 从数组里面取第12条信息
//$info = $code[13];

// 取出姓名
//$name = $info ->name;
//echo $name;


function send_post($url, $post_data)
{
    $postdata = http_build_query($post_data);
    $options = array(
        'http' => array(
            'method' => 'POST',
            'header' => 'Content-type:application/x-www-form-urlencoded',
            'content' => $postdata,
            'timeout' => 15 * 60 // 超时时间(单位:s)
        )
    );
    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    return $result;
}

Aes工具类

<?php
// 使用AES解密
//$aes = new Aes();
//$decode = $aes ->encrypt('Hello World!');
//echo "decode: ". $decode;

/**
 * aes 加密 解密类库
 * Class Aes
 */
class Aes {
    private $key = null;

    /**
     *
     * @param $key 		密钥
     * @return String
     */
    public function __construct() {
        // 需要小伙伴在配置文件app.php中定义aeskey
        $this->key = 'key';
    }

    /**
     * 加密
     * @param String input 加密的字符串
     * @param String key   解密的key
     * @return HexString
     */
    public function encrypt($input) {
        $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $input = $this->pkcs5_pad($input, $size);
        $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        mcrypt_generic_init($td, $this->key, $iv);

        $data = mcrypt_generic($td, $input);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        $data = base64_encode($data);

        return $data;

    }
    /**
     * 填充方式 pkcs5
     * @param String text 		 原始字符串
     * @param String blocksize   加密长度
     * @return String
     */
    private function pkcs5_pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    /**
     * 解密
     * @param String input 解密的字符串
     * @param String key   解密的key
     * @return String
     */
    public function decrypt($sStr) {
        $decrypted= mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$this->key,base64_decode($sStr), MCRYPT_MODE_ECB);
        $dec_s = strlen($decrypted);
        $padding = ord($decrypted[$dec_s-1]);
        $decrypted = substr($decrypted, 0, -$padding);

        return $decrypted;
    }

}
  • 使用 PHP 访问我们的接口后,成功获取到了加密数据,在解码并打印出字符串中的每个字符后,发现字符串的最后多个几个ASC码值为0的字符,百度了一下是空值的意思.
  • 在使用了 PHP 自带的 trim() 方法对解密字符串进行过滤后,发现转 JSON 成功了.
  • 一开始以为这些空值的出现是 AES 加密工具自身的问题,后来直到看了自己的 AES 工具类中的 JAVA 代码,不禁泪流满面,[在最后补0],这哪是补0,这是给我补刀啊.
  • 在移除了这段补 0 的代码后,一切正常了,这个问题搞了我差不多3天,心态爆炸.

JAVA AES 工具类加密代码

private static String _encrypt(String sSrc, String sKey) throws Exception {
        if (StringUtils.isBlank(sKey)) {
            Util.log.error("Key为空null");
            throw new ServiceException("Key为空");
        }
        if (sKey.length() != 16) {//判断key是否为16位  
            Util.log.error("Key的长度不是16位");
            throw new ServiceException("Key的长度不是16位");
        }
        byte[] raw = sKey.getBytes("ASCII");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

        byte[] srawt = sSrc.getBytes("UTF-8");
        int len = srawt.length;
        //计算补0后的长度
        while (len % 16 != 0) {
            len++;
        }
        byte[] sraw = new byte[len];
        //在最后补0
        for (int i = 0; i < len; ++i) {
            if (i < srawt.length) {
                sraw[i] = srawt[i];
            } else {
                sraw[i] = 0;
            }
        }
        byte[] encrypted = cipher.doFinal(sraw);
        //此处使用BASE64做转码功能,同时能起到2次加密的作用
        String originalString = Base64Utils.encodeToString(encrypted);
        originalString = originalString.replaceAll("\n", "");
        originalString = originalString.replaceAll("\r", "");
        return originalString;
    }

PS. 这个工具类是复制的前人的,我错了,下次我再也不做伸手党了,另外 JAVA代码没有出现这样的问题是因为 JAVA 代码解密时已经做了 trim()处理,如果不做处理也是有问题的.
在这里插入图片描述

总结

  • 别人的代码好用是好用,就是出了问题应该第一个就要联想到它,否则就像我一样,浪费时间不说,还严重影响开发心态.
  • 对于 AES 加密原理,和 Base64 加密原理,推荐两篇文章,说的很详细,可以看下面的参考链接.

参考

十分钟读懂AES加密算法
什么是Base64?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值