1.安装Dingo
你必须在你的项目中修改 composer.json
文件并且运行 composer update
命令来加载这个包(别问我 composer require dingo/api 为啥不能安装,我也不知道,尽量用composer require 命令安装,如果安装不了就用 composer update 命令)
"require": {
"dingo/api": "2.3"
}
使用以下命令可以发布 API 的配置文件到 config 文件夹下
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
在 .env
文件下加入
API_STANDARDS_TREE=vnd //环境
API_SUBTYPE=sdk //子类型
API_PREFIX=api //前缀
API_VERSION=v1//版本
API_NAME="SDK"//名字
API_CONDITIONAL_REQUEST=false//带条件的请求
API_STRICT=false//严格模式
API_DEFAULT_FORMAT=json//响应格式
API_DEBUG=true//调试模式
注意:.env配置文件是不能有空格和注释的!
创建端点(API控制器的路由写在这里)通常在routes/api.php里
$api = app('Dingo\Api\Routing\Router');
$api->version('v1', function ($api) {
});
2.安装JWT
composer require tymon/jwt-auth 1.*@rc
进行一些配置
这里指的注意的是,有些文档会说要添加 Tymon\JWTAuth\Providers\LaravelServiceProvider::class ,
这只在 Laravel 5.4 及以下版本是必要的,更新的 Laravel 版本无需添加。
还有一些文档说要添加 Tymon\JWTAuth\Providers\JWTAuthServiceProvider
这是很久以前的 JWT 版本的(大概 0.5.3 以前的版本)
发布配置文件
# 这条命令会在 config 下增加一个 jwt.php 的配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
生成加密密钥
# 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar
php artisan jwt:secret
更新你的模型
<?php
namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject # 这里别忘了加
{
use Notifiable;
protected $table = "";//表名
protected $primaryKey = "";//主键
public $timestamps = false;
protected $fillable = [];//白名单
protected $guarded = [];//黑名单
// Rest omitted for brevity
/**
* 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
config/app.php文件里
'aliases' => [
...
// 添加以下两行
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],
修改 auth.php
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt', // 原来是 token 改成jwt
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Api\Models\PlayerApplication::class, //改成自己刚定义的模型
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
修改api.php
config/api.php
'auth' => [
'jwt' => Dingo\Api\Auth\Provider\JWT::class
],
获取token控制器
<?php
/**
* 用户注册即登录 并分配TOKEN 和用户登录并分配token
*/
namespace App\Api\Controllers\V1;
use App\Http\Controllers\Controller;
use App\Models\Pusers;
use Dingo\Api\Routing\Helpers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Tymon\JWTAuth\Facades\JWTAuth;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use App\Service\GeneralMethod;
class RegisterController extends Controller
{
//dingoAPI响应生成器
use Helpers;
//底层小方法引用
use GeneralMethod;
public function __construct() {
$this->middleware('auth:api', ['except' => ['init','login']]);
// $this->middleware('auth:api');
}
/**
* @param Request $request
* @return array|\Illuminate\Http\JsonResponse
* //注册并登录
*/
public function init(Request $request)
{
$User_data = $request->all();
//校验用户名密码格式
$validator = $this->validator($User_data);
if($validator->fails()){
return $this->returnErrorMessage(['status'=>'error','message'=>301]);
}
$init = [
'nickname'=>$this->nickNameRand(8),
'mobile'=>$User_data['mobile'],
//密码是bcrypt方法加密的下面这个用的是laravel自带的bcrypt加密方法
'password'=>HASH::make($User_data['password']),
];
//插入数据库
Pusers::Create($init);
//验证账号密码,并生成token的方法在GeneralMethod性状中
return $this->createToken($User_data);
}
/**
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
* 登录
*/
public function login(Request $request)
{
$data = $request->all();
//校验用户名密码格式
$validator = $this->loginValidator($data);
if($validator->fails()){
return $this->returnErrorMessage(['status'=>'error','message'=>301]);
}
return $this->createToken($request->all());
}
//注册验证参数
public function validator(array $data)
{
return Validator::make($data,[
'mobile'=>'required|regex:/^1[345789][0-9]{9}$/|unique:p_users,mobile',//手机号格式验证,手机号唯一
'password'=>'required|between:6,12|alpha_num'//密码6位数到12位数,由字母和数字构成
]);
}
//登录验证参数
public function loginValidator(array $data)
{
return Validator::make($data,[
'mobile'=>'required|regex:/^1[345789][0-9]{9}$/',//手机号格式验证,手机号唯一
'password'=>'required|between:6,12|alpha_num'//密码6位数到12位数,由字母和数字构成
]);
}
}
service目录里的GeneralMethod.php里的检验账号密码并生成token的方法
<?php
namespace App\Service;
use App\Models\Pusers;
/**
* Class Service
* @package App\Service
* 通用底层小方法
*/
Trait GeneralMethod
{
/**
* @param array $data
* API返回错误码对应信息
*/
public function returnErrorMessage(array $data)
{
$data['message'] = config('apierror.'.$data['message']);
return $data;
}
/**
* 随机用户名生成
*/
public function nickNameRand($length)
{
// 密码字符集,可任意添加你需要的字符
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for($i = 0; $i < $length; $i++)
{
// 这里提供两种字符获取方式
// 第一种是使用 substr 截取$chars中的任意一位字符;
// 第二种是取字符数组 $chars 的任意元素
// $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $str;
}
/**
* @param array $data
* @return array|\Illuminate\Http\JsonResponse
* 检验用户名密码生成token
*/
protected function createToken(array $data)
{
/**
* 校验用户名密码并生成token(会将password字段进行bcrypt加密并检测 密码字段名必须是password,attempt方法必须传的是数组)
*/
if (! $token = auth('api')->attempt($data)) {
return $this->returnErrorMessage(['status'=>'error','message'=>401]);
}
$user = Pusers::where('mobile',$data['mobile'])->first();
//注册并登录成功返回信息
return $this->respondWithToken($user->nickname,$token);
}
/**
* @param object $data
* @return array|\Illuminate\Http\JsonResponse
* 利用用户信息生成token(有时候你需要进行手机短验登录,第三方授权登录,这时候在控制器完成了授权登录的逻辑,然后把用户信息传过来生成token)
*/
protected function createToken($data)
{
/**
* 生成token(attempt方法换成login方法就好 login方法传入的是用户信息的对象,例如:User::first()查出来的对象)
*/
if (! $token = auth('api')->login($data)) {
return $this->returnErrorMessage(['status'=>'error','message'=>401]);
}
//注册并登录成功返回信息
return $this->respondWithToken($data->nickname,$token);
}
//返回方法
protected function respondWithToken($data,$token)
{
return response()->json([
'status'=>200,
'message'=>$data,
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60
]);
}
}
JWT token 无痛刷新实现(定义一个中间件)
执行下面的命令生成中间件
php artisan make:middleware RefreshJwtToken
<?php
namespace App\Http\Middleware;
use Closure;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class RefreshJwtToken extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
//检查请求中是否带有token 如果没有token值则抛出异常
$this->checkForToken($request);
try{
//从请求中获取token并且验证token是否过期 若是不过期则请求到控制器处理业务逻辑 若是过期则进行刷新
if ($request->user = JWTAuth::parseToken()->authenticate()) {
return $next($request);
}
throw new UnauthorizedHttpException('jwt-auth', '未登录');
}catch (TokenExpiredException $exception){
// 此处捕获到了 token 过期所抛出的 TokenExpiredException 异常,我们在这里需要做的是刷新该用户的 token 并将它添加到响应头中
try{
// 刷新用户的 token
$token = $this->auth->refresh();
// 使用一次性登录以保证此次请求的成功
Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
}catch (JWTException $exception){
// 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
}
}
//将token值返回到请求头
return $this->setAuthenticationHeader($next($request), $token);
}
}
在Kernel中注册中间件
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
......
'token.refresh' => \App\Http\Middleware\RefreshJwtToken::class,
];
在路由中使用这个中间件就可以实现无痛刷新 (当然还需要前端配合跟换 token 值,后端是已经完成啦)
JWTAuth.php中方法
attempt() 尝试对用户进行身份验证并返回令牌
authenticate() 通过令牌对用户进行身份验证
user() 获取经过身份验证的用户
参考链接
DingoAPI文档
jwt完整详解
生成token的时候返回false解决
Laravel+JWT token 无痛刷新实现
Laravel+JWT token 无痛刷新实现
jwt-auth 官方 wiki
laravel 框架bcrypt加密方式的密码校验