这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战
搭建 JWT 认证体系
JWT 是一个典型的前后端分离式开发的应用,将采用 JWT(JSON-Web-Tokens)认证,JWT 是一个非常轻巧的认证规范,其主要作用场景为:认证和数据交换。就好比出门旅游入驻酒店一样,进入房间需要房卡进行认证后,你才能进去,而房卡要事先经过授权获取。
使用 composer 安装
执行 composer 命令:
composer require tymon/jwt-auth 1.*@rc
这里时间可能稍微长点,请耐心等待,再去喝杯咖啡吧。
开启中间件认证
打开 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 的密钥,注意,这个不能随意修改。
更新模型
打开 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;
}
}