Laravel 实现 API 通讯数据的加解密

在与APP的api通讯中,经常会将敏感数据进行加密传输,然后再解密进行处理。在项目开发的时候,也会封装方法去对数据进行加解密,那么我们在laravel中能否在保持代码优雅度的情况下实现加解密呢?
当然可以,下面是我通过middlewareResponse::macro实现加解密的方案。

请求的解密

对请求数据进行处理,首选当然是在middleware中进行。

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Contracts\Encryption\DecryptException;
    use Illuminate\Contracts\Encryption\Encrypter;
    use Illuminate\Http\Request;
    use Symfony\Component\HttpFoundation\ParameterBag;

    class DecryptApiRequest
    {
        /**
         * @var Encrypter
         */
        protected $encrypter;

        public function __construct(Encrypter $encrypter)
        {
            $this->encrypter = $encrypter;
        }

        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            try {
                $content = $this->decrypt($request->getContent());
            } catch (DecryptException $exception) {
                return abort(403);
            }

            return $next($this->putIn($request, $content));
        }

        /**
         * decrypt the content
         * @param string $content
         * @return string
         */
        protected function decrypt(string $content)
        {
            return $this->encrypter->decrypt($content, false);
        }

        /**
         * put the decrypt data into request
         * @param Request $request
         * @param string $content
         * @return Request
         */
        protected function putIn(Request $request, string $content)
        {
            if ($request->getContentType() === 'json') {
                $request->setJson(new ParameterBag((array) json_decode($content, true)));
            } else {
                $request->attributes = new ParameterBag([$request->getContentType() => $content]);
            }

            return $request;
        }
    }

该中间件中添加了decrypt()putIn()方法,其中decrypt()用于处理数据的解密,putIn() 用于将解密后的数据放入Request对象用,供后面访问使用。由于加密数据请求一般是采用文本的形式在body中进行发送,所以这里在获取到解密的数据之后,根据contentType类型存入了Request对象中,如果是json则存入了 Requestjson 属性中, 如果是其他类型,则以contentType为键 content为值的形式存入了attributes中。

于是乎,解密完成了,并且也不会遗弃Request对象,不会因为在Reqeust中取不到解密后的数据而放弃他。
怎么获取数据?
json类型直接使用 Request::get($key);
text 类型使用 Request::get('txt');
其他类型,Reqeust::getContentType()看看就知道了。

响应的加密

这里用的是响应宏进行了处理。具体请看响应的文档 【响应宏】
代码如下:

namespace App\Providers;

use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;

class ResponseMacroServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Response::macro('encrypt', function ($value) {

            if (is_array($value)) $value = json_encode($value);

            if ($value instanceof Jsonable) $value = $value->toJson();

            return Response::make(app()->make(Encrypter::class)
                ->encrypt((string) $value, false));
        });
    }

    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

在加密数据之前,首先对数据类型进行了处理,最终会将数据转化为string类型进行加密。
使用时,直接调用encrypt()方法即可。

Route::post('/', function (\Illuminate\Http\Request $request) {
    return response()->encrypt(['a', 'b']);
});
本作品采用《CC 协议》,转载必须注明作者和本文链接
打酱油
本帖由系统于 2年前 自动加精
sureyee
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 10

像这种和客户端(非机器)的通信,对称加密是没有意义的。因为密钥是公开的。
非对称加密才可行一点。比如rsa

2年前 评论
赵佳

app怎么解密呢?

2年前 评论
sureyee

@赵佳 看下laravel解密的源码,Illuminate\Encryption\Encrypter laravel默认使用的是这个驱动,主要是通过openssl_decrypt进行解码的,app端也有openssl加解密的包,不过key默认情况下是app_key中base64_decode之后的内容。

2年前 评论
sureyee

其实在middleware中,$next($request)返回的是一个Response 这里是指你没有修改middleware中返回类型的情况,默认返回的就是你在控制器中返回的Response。具体原理可参见Pipelinethen()carry()方法。所以在这里我们可以更加优美的进行处理,那就是直接在middleware中封装一个encrypt()方法,对Responsecontent进行加密,这样开发者就不需要关注是否需要调用response()->encrypt($data) 方法,而是直接返回response($data)即可。
更多方法,待大家去发现,这里的代码只作为参考,并没有做数据上的测试。

2年前 评论
农村闲散劳动力

重点在于采用了那种加密算法

2年前 评论
sureyee

@赵聪 默认使用的是AES-256-CBC

2年前 评论

像这种和客户端(非机器)的通信,对称加密是没有意义的。因为密钥是公开的。
非对称加密才可行一点。比如rsa

2年前 评论

@bluegeek 是的,应该用非对称加密

2年前 评论
wenber

@bluegeek laravel 加密后还会再使用消息码签名的.

2年前 评论

@ziyanziyu 签名的意义在于别人不知道你用来签名的密钥 其实和加密是一样的。用在客户端的一切都是公开的。所以签名也没有必要。签名都是用于机器和机器之间的。比如你可以看一下微信开发,微信服务器和自己的服务器,两边都是用同一个client secret验证的。而client secret别人是不知道的。

2年前 评论
sureyee

@bluegeek 在app通讯中使用非对称加密是好一些,这样appkey不会暴露出来,对项目的安全性也会很高。这里主要是说明如何使用中间件和响应宏进行处理加解密。如果要使用非对称加密,也可以自己封装一个非对称加密算法,注入到项目中。

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!