阅读 330

实战:使用 Lumen 和 Vue 构建个人清单应用(二)搭建认证体系和接口规范

这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战

搭建 JWT 认证体系

JWT 是一个典型的前后端分离式开发的应用,将采用 JWT(JSON-Web-Tokens)认证,JWT 是一个非常轻巧的认证规范,其主要作用场景为:认证和数据交换。就好比出门旅游入驻酒店一样,进入房间需要房卡进行认证后,你才能进去,而房卡要事先经过授权获取。

使用 composer 安装

执行 composer 命令:

composer require tymon/jwt-auth 1.*@rc
复制代码

这里时间可能稍微长点,请耐心等待,再去喝杯咖啡吧。 image.png

开启中间件认证

打开 bootstrap/app.php,并取消以下四处的注释: 开启 Facade。

$app->withFacades();
复制代码

开启 Eloquent。

$app->withEloquent();
复制代码

开启中间件。

$app->routeMiddleware([
    'auth' => App\Http\Middleware\Authenticate::class,
]);
复制代码

注册中间件。

$app->register(App\Providers\AuthServiceProvider::class);
复制代码

然后注册中间件那行后面追加服务提供者。

$app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);
复制代码

生成加密密钥

打开终端执行:

php artisan jwt:secret
复制代码

运行成功后会在 .env 文件中新增一行 jwt 的密钥,注意,这个不能随意修改。 image.png

更新模型

打开 appUser.php,更新 User 模型为以下内容:

  • 添加 JWTSubject 并继承。
  • 添加 getJWTIdentifier 和 getJWTCustomClaims。
<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
use Laravel\Lumen\Auth\Authorizable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
    use Authenticatable, Authorizable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email',
    ];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = [
        'password',
    ];

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

复制代码

注册 Facade

修改 bootstrap/app.php 中的 $app->withFacades();

$app->withFacades(true, [
    'Tymon\JWTAuth\Facades\JWTAuth' => 'JWTAuth',
    'Tymon\JWTAuth\Facades\JWTFactory' => 'JWTFactory',
]);
复制代码

设置 auth.php

在项目根目录新建 config 文件夹,并新建 auth.php。 写入以下内容: 这里是直接复制 \vendor\laravel\lumen-framework\config\auth.php中的内容并加以修改的。

<?php

return [

    'defaults' => [
        'guard' => env('AUTH_GUARD', 'api'),
    ],

    'guards' => [
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => \App\User::class,
        ],
    ],

    'passwords' => [
        //
    ],

];
复制代码

解决跨域问题

在 appHttpMiddleware 下新建 CorsHttp.php 文件,并写入:

<?php

namespace App\Http\Middleware;

use Illuminate\Http\Request;
use Illuminate\Http\Response;

class CorsHttp
{
    private $headers;
    private $allow_origin;

    public function handle(Request $request, \Closure $next)
    {
        $this->headers = [
            'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE',
            'Access-Control-Allow-Headers' => $request->header('Access-Control-Request-Headers'),
            'Access-Control-Allow-Credentials' => 'true', //允许客户端发送cookie
            'Access-Control-Max-Age' => 1728000 //该字段可选,用来指定本次预检请求的有效期,在此期间,不用发出另一条预检请求。
        ];

        $this->allow_origin = [
            // 'http://localhost',
            // 'http://192.168.1.17:8080'
        ];
        $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';

        //如果origin不在允许列表内,直接返回403
        // if (!in_array($origin, $this->allow_origin) && !empty($origin))
        //     return new Response('Forbidden', 403);
        //如果是复杂请求,先返回一个200,并allow该origin
        if ($request->isMethod('options'))
            return $this->setCorsHeaders(new Response('OK', 200), $origin);
        //如果是简单请求或者非跨域请求,则照常设置header
        $response = $next($request);
        $methodVariable = array($response, 'header');
        //这个判断是因为在开启session全局中间件之后,频繁的报header方法不存在,所以加上这个判断,存在header方法时才进行header的设置
        if (is_callable($methodVariable, false, $callable_name)) {
            return $this->setCorsHeaders($response, $origin);
        }
        return $response;
    }

    /**
     * @param $response
     * @return mixed
     */
    public function setCorsHeaders($response, $origin)
    {
        foreach ($this->headers as $key => $value) {
            $response->header($key, $value);
        }
        // if (in_array($origin, $this->allow_origin)) {
        $response->header('Access-Control-Allow-Origin', $origin);
        // $response->header("Access-Control-Allow-Headers", "x-requested-with,Authorization,Content-Type");
        // } else {
        // $response->header('Access-Control-Allow-Origin', '');
        // }
        return $response;
    }
}
复制代码

在 bootstrapapp.php 中加入:

$app->middleware([
    \App\Http\Middleware\CorsHttp::class,
]);
复制代码

制定 API 接口规范

请求方式规范

我们规范获取数据的接口统一用 GET 方式,修改/新增/删除的操作,统一用 POST 方式请求。

GET(SELECT):    从服务器取出资源(一项或多项)。
POST(CREATE):   在服务器修改/新增/删除一个资源。
复制代码

接口路径规范

我们按照域名+模块名+方法名来定义一个接口地址,例如在我们的项目中,需要从用户模块(user)中查询用户信息(getUserInfo),则接口地址为:

/user/getUserInfo
复制代码

返回数据结构规范

{
  code: 0, // 0:操作/获取数据成功,其它:失败
  data: {} || null,
  msg: ''
}
复制代码

状态码规范

如果 http 状态码返回200,那么就可以按照上面的结构返回业务数据,其次我们规定以下几个常用的状态码和处理方式:

400: Bad Request 页面提示
500:Internal Server Error  页面提示
401:Unauthorized 返回登录
403:Forbidden 页面提示
404:Not Found 页面提示
复制代码

封装一些工具类

在 app 目录中创建 Lib 目录,用于存放我们后续会封装的类库,并新建 Util.php 文件。 我们先根据需求封装两个方法,一个用于随机生成 ID (createId),一个用于前端 Ajax 数据返回(ajaxMsg)。

<?php

namespace App\Lib;

/**
 * Util class
 */
class Util
{
    /**
     * createUid function
     * 生成ID,大于6位,默认11位
     * @param int $len
     * @return string
     */
    public static function createId($len = 11)
    {
        if ($len < 6 || $len > 11) {
            return false;
        }
        // ID首位不为0
        $first = mt_rand(1, 9);
        $other = '';
        for ($i = 0; $i < $len - 1; $i++) {
            $other .= mt_rand(0, 9);
        }
        return $first . $other;
    }

    /**
     * ajaxMsg function
     * 网络请求数据封装返回Response
     * @param [string] $code 返回code
     * @param [string] $msg 返回消息提示
     * @param [string] $data 返回数据包
     * @param [int] $status_code 返回状态码
     * @return JSON
     */
    public static function ajaxMsg($code = '', $msg = '', $data = '', $status_code = 200)
    {
        return response()
            ->json([
                'code' => $code, // 返回状态码
                'data' => $data, // 返回数据
                'msg' => $msg, // 返回提示
            ], $status_code)->setEncodingOptions(JSON_UNESCAPED_UNICODE);
    }
    
  	/**
     * isPsw function
     * 密码校验
     * 必须包含大小写字母和数字的组合,不能使用特殊字符,长度在6-10之间
     * @param [type] $data
     * @return bool
     */
    public static function isPsw($data)
    {
        $reg = '/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/';
        return preg_match($reg, $data) ? true : false;
    }
}
复制代码

欢迎阅读其它文章

文章分类
前端
文章标签