1 什么是dingo
dingo api包是给laravel和lumen提供的Restful的工具包,它可以与jwt组件一起配合快速的完成用户认证,同时对于数据和运行过程中所产生的异常能够捕获到并且可以做出对应的响应。
主要功能:
- Router Version 路由版本管理
- http Exception 异常处理
- response transform 转化响应格式
1 安装dingo
在laravel根目录下通过composer进行dingo扩展包的安装,具体命令如下:
composer require api-ecosystem-for-laravel/dingo-api
使用以下命令可以发布 API 的配置文件到 config 文件下:
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
2 配置dingo
关于dingo的api配置信,我们可以在.env文件中进行配置
# dingo
# API_SUBTYPE —— 项目的简称;
API_SUBTYPE=lms
# API_PREFIX —— 与 API_DOMAIN 二选一,路由的前缀,例如设置为 api
API_PREFIX=api
# 定义版本
API_VERSION=v1
# 是否开启调试模式
API_DEBUG=true
关于dingo的详细配置请查看相关文档:https://learnku.com/docs/dingo-api/2.0.0/Configuration/1444
2 什么是JWT
jwt全称JSON Web Tokens ,是一个非常轻巧的规范,这个规范允许我们使用jwt在用户和服务器之间传递安全可靠的信息,他的主要使用场景为:认证与数据交换
1 安装JWT
在laravel根目录下通过composer进行jwt扩展包的安装,具体命令如下:
composer require tymon/jwt-auth
使用以下命令可以发布 API 的配置文件到 config 文件下:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
2 配置JWT
在.env文件中生成jwt加密秘钥,具体命令如下:
php artisan jwt:secret
修改config/api.php配置
'auth' => [
'jwt' => 'Dingo\Api\Auth\Provider\JWT',
],
修改config/auth.php配置
'defaults' => [
#注:这里修改改了默认的配置,默认是web
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
],
关于jwt的详细配置请查看相关文档:https://jwt-auth.readthedocs.io/en/develop/
3 相关代码演示
创建RefreshToken中间件,用于令牌过期刷新
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class RefreshToken extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
// 检查此次请求中是否带有 token,如果没有则抛出异常。
$this->checkForToken($request);
// 使用 try 包裹,以捕捉 token 过期所抛出的 TokenExpiredException 异常
try {
// 检测用户的登录状态,如果正常则通过
if ($this->auth->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);
}
}
User模型需要实现两个方法:getJWTIdentifier()和getJWTCustomClaims()(注意接口JWTSubject)
'datetime',
];
/**
* 指示是否自动维护时间戳
*
* @var bool
*/
public $timestamps = false;
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
?>
创建UserController用于鉴权等相关操作
middleware('refresh.token', [
'except' => [
'login',
],
]);
}
/**用户登录
* @param Request $request
* @return \Illuminate\Http\JsonResponse|void
*/
public function login(Request $request)
{
$phone = $request->get('phone');
$user = User::where('phone', $phone)->first();
// //attempt貌似无法验证其他字段,如需用其他字段鉴权使用login()
// $credentials = request(['name','password']);
// if (!$token = auth()->attempt($credentials)) {
// return response()->json(['error' => 'Unauthorized'], 401);
// }
//只要是user实例就可以通过login鉴权
if (! $token = auth()->login($user)) {
return response()->json([
"restful" => false,
"message" => "账号错误",
]);
}
//获取用户信息
$user = $this->user();
$key = "user::info::".$user->id;
//Redis缓存用户信息3600秒
Redis::set($key,serialize($user->original),"EX",3600);
return $this->respondWithToken($token);
}
/**获取用户
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function user()
{
return response()->json(auth()->user());
}
/**用户退出
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(["message" => "退出成功"]);
}
/**用户登录状态刷新
* Refresh a token.
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**返回值
* @param $token
* @return array
*/
protected function respondWithToken($token)
{
return [
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => auth()->factory()->getTTL() * 60,
'restful' => true
];
}
}