API 鉴权新姿势 - 签名鉴权

laravel-api-auth

laravel API 鉴权

这是一个 laravel 的 API 鉴权包, laravel-api-auth 采用密钥加密的鉴权方式,只要客户端不被反编译从而泄露密钥,该鉴权方式理论上来说是安全的。

项目地址: github.com/96qbhy/laravel-api-auth
star

安装

composer require 96qbhy/laravel-api-auth

配置

  1. 注册 ServiceProvider:

    Qbhy\LaravelApiAuth\ServiceProvider::class,

    laravel 5.5+ 版本不需要手动注册

  2. 发布配置文件

    php artisan vendor:publish --provider="Qbhy\LaravelApiAuth\ServiceProvider"
  3. App\Http\Kernal 中注册中间件

    protected $routeMiddleware = [
        'api_auth' => Qbhy\LaravelApiAuth\LaravelApiAuthMiddleware::class,
        // other ...
    ];
  4. 添加 role

    php artisan api_auth

    然后按照格式把 access_keysecret_key 添加到, config/api_auth.php 里面的 roles 数组中。

    'roles' => [
        '{access_key}' => [
            'name' => '{role_name}',        // 角色名字,例如 android
            'secret_key' => '{secret_key}',
        ],
    ],
  5. 自定义签名方法 (可选)
    config/api_auth.php 中的 encrypting 可以修改为自定义的签名函数,该函数将传入三个参数: 密钥: $secret_key、随机字符串: $echostr、时间戳: $timestamp,返回签名后的字符串。该函数默认为:

    /**
     * @param $secret_key
     * @param $echostr
     * @param $timestamp
     * @return string
     */
    function encrypting($secret_key, $echostr, $timestamp) {
        return md5($secret_key . $echostr . $timestamp);
    }
  6. 自定义签名校验规则(可选)
    config/api_auth.php 中的 rule 可以修改为自定义的校验函数,该函数将传入三个参数: 密钥: $secret_key、客户端签名: $signature、服务端签名: $server_signature,必须返回布尔值。该函数默认为:

     /**
      * @param $secret_key
      * @param $signature
      * @param $server_signature
      * @return bool
      */
     function rule($secret_key, $signature, $server_signature)
     {
         return $signature === $server_signature;
     }
  7. 自定义错误处理(可选)
    config/api_auth.php 中的 error_handler 可以修改为自定义的错误处理函数,该函数将传入两个参数: 请求: $request、错误码: $code。该函数默认为:

     /**
      * @param Request $request
      * @param int $code
      * @return \Illuminate\Http\JsonResponse
      */
     function error_handler($request, $code)
     {
         return response()->json([
             'msg' => 'Forbidden',
             'code' => $code
         ], 403);
     }  

    $code 可能是以下几个值中的一个:

    • LaravelApiAuthMiddleware::LACK_HEADER -> 缺少请求头。
    • LaravelApiAuthMiddleware::ACCESS_KEY_ERROR -> access_key 错误。
    • LaravelApiAuthMiddleware::SIGNATURE_ERROR -> 签名错误。
    • LaravelApiAuthMiddleware::SIGNATURE_LAPSE -> 签名失效,客户端签名时间和服务端签名时间差超过设置的 timeout 值。
    • LaravelApiAuthMiddleware::SIGNATURE_REPETITION -> 签名重复,规定时间内出现两次或以上相同的签名。

使用

路由中

Route::get('api/example', function(Request $request){
    // $request->get('client_role');
    // todo...
})->middleware(['api_auth']);
\\ or
Route::group(['middleware'=>'api_auth'], function(){
    // routes...
});

通过验证后 $request 会添加一个 client_role 字段,该字段为客户端的角色名称。

前端

import axios from 'axios';

const access_key = '{access_key}';  // 服务端生成的 access_key
const secret_key = '{secret_key}';  // 服务端生成的 secret_key

const timestamp = Date.parse(new Date()) / 1000;    // 取时间戳
const echostr = 'asldjaksdjlkjgqpojg64131321';      // 随机字符串自行生成

function encrypting(secret_key, echostr, timestamp){
    return md5(secret_key + echostr + timestamp);    // md5 库自行引入
}

const requestConfig = {
    headers: {
        "api-signature": encrypting(secret_key, echostr, timestamp),
        "api-echostr": echostr,
        "api-timestamp": timestamp,
        "api-access-key": access_key
    }
};

axios.post('/api/example',{},requestConfig).then(res=>{
    // todo
});

本例子为 web 前端的例子,其他客户端同理,生成签名并且带上指定参数即可正常请求。
通过自定义签名方法和自定义校验方法,可以使用其他加密方法进行签名,例如 哈希 等其他加密算法。更多自定义可以直接复制 Qbhy\LaravelApiAuth\LaravelApiAuthMiddleware 中间件后自行修改 。有问题请开 issue

96qbhy.com
96qbhy@gmail.com

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
qbhy
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 23
qbhy

自己顶一个。

6年前 评论
leo

然而大多数情况反编译客户端的成本很低

6年前 评论

网页web端应该是明文啊~~ 这个key ~~不等于直接知道了么

6年前 评论
qbhy

@leo ios 不考虑,安卓端加了盐 + 混淆加密啥的,反编译难度其实没有想象中的那么简单。而且,密钥这种东西可以编译成
.so 后缀的动态库,即使反编译了APP,也不用担心密钥泄露的,这种加密方法,即使知道了加密算法,没有密钥,也没有用的。

6年前 评论
qbhy

@依剑听雨 所以 web 端的 API 通常都比较简单,加密方式也不适用这种密钥方式,还有就是 web 端的 API 通常要跟 APP 的
API 区分开来,像是 微信网页版 的 API 能做的事情就非常有局限性,正是因为 web 端没有绝对安全(或者说相对安全)的 API 鉴权方法。

6年前 评论
leo

另外这个叫“签名”比叫“加密”合适

6年前 评论
qbhy

@leo 谢谢建议,之前我也在纠结呢。

6年前 评论
qbhy

顶一顶

6年前 评论

laravel5后直接用自带passport做接口,如果允许的话再配合Spatie或者Bouncer权限包扩展下,鉴权挺方便的,不过你的方法快捷,之前做个和你差不多的,不过是走在request类上面,如下:

    Request::macro('check_sign', function($key, ...$inputs) {
        if ( $inputs ) {
            $data = Request::only(...$inputs);
        } else {
            $data = Request::except('sign');
        }
        ksort($data);
        $data['key'] = $key;
        return Request::input('sign') === md5(urldecode(http_build_query($data)));
    });

例如在控制器需要用到则判断 $request->check_sign('密钥')即可,中间件也一样。

6年前 评论
qbhy

@ab0029 嗯,方法差不多。中间统一处理错误比较方便一点。

6年前 评论

@ab0029 http_build_query 有个问题,如果其中一项为 null 会丢失掉

http_build_query(['a'=>null,'b'=>1,'c'=>false]);//b=1&c=0

不过这里 Request 取的应该都成字符串了

6年前 评论
qbhy

顶顶更健康

6年前 评论

@96qbhy 这样的签名防止不了篡改请求参数

5年前 评论
qbhy

@jake_zou 这个是为了防止没有权限的调用接口做的,防止篡改参数还是建议 https 或者 jwt。

5年前 评论
qbhy

@BigPark 我也在laravel学院发过,并且允许学院君转载了。看内容就知道作者是我啊。

4年前 评论
qbhy

由于时间原因,此项目已不建议大家使用了。如果有类似需求的可以评论区讨论。

4年前 评论

作者 这个包现在还能使用吗?

4年前 评论
qbhy (楼主) 4年前
maltose (作者) 4年前

没有做判空和字段的合法性检测,不知道是不需要还是...

file

3年前 评论

用不了了

7个月前 评论

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