Laravel 实现 API 通讯数据的加解密
在与APP的api通讯中,经常会将敏感数据进行加密传输,然后再解密进行处理。在项目开发的时候,也会封装方法去对数据进行加解密,那么我们在laravel中能否在保持代码优雅度的情况下实现加解密呢?
当然可以,下面是我通过middleware和Response::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则存入了 Request 的 json 属性中, 如果是其他类型,则以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 协议》,转载必须注明作者和本文链接
关于 LearnKu
高认可度评论:
像这种和客户端(非机器)的通信,对称加密是没有意义的。因为密钥是公开的。
非对称加密才可行一点。比如rsa
app怎么解密呢?
@赵佳 看下laravel解密的源码,
Illuminate\Encryption\Encrypterlaravel默认使用的是这个驱动,主要是通过openssl_decrypt进行解码的,app端也有openssl加解密的包,不过key默认情况下是app_key中base64_decode之后的内容。其实在
middleware中,$next($request)返回的是一个Response这里是指你没有修改middleware中返回类型的情况,默认返回的就是你在控制器中返回的Response。具体原理可参见Pipeline的then()和carry()方法。所以在这里我们可以更加优美的进行处理,那就是直接在middleware中封装一个encrypt()方法,对Response的content进行加密,这样开发者就不需要关注是否需要调用response()->encrypt($data)方法,而是直接返回response($data)即可。更多方法,待大家去发现,这里的代码只作为参考,并没有做数据上的测试。
重点在于采用了那种加密算法
@赵聪 默认使用的是
AES-256-CBC像这种和客户端(非机器)的通信,对称加密是没有意义的。因为密钥是公开的。
非对称加密才可行一点。比如rsa
@bluegeek 是的,应该用非对称加密
@bluegeek laravel 加密后还会再使用消息码签名的.
@ziyanziyu 签名的意义在于别人不知道你用来签名的密钥 其实和加密是一样的。用在客户端的一切都是公开的。所以签名也没有必要。签名都是用于机器和机器之间的。比如你可以看一下微信开发,微信服务器和自己的服务器,两边都是用同一个client secret验证的。而client secret别人是不知道的。
@bluegeek 在app通讯中使用非对称加密是好一些,这样appkey不会暴露出来,对项目的安全性也会很高。这里主要是说明如何使用中间件和响应宏进行处理加解密。如果要使用非对称加密,也可以自己封装一个非对称加密算法,注入到项目中。