laravel一直是被大家说是非常优雅的框架,这次无所事事也就翻开文档看看,并且之后也使用laravel5.8适配了一个reactAdmin后台管理系统,前后端分离,所以决定使用JWT。
原本在thinkPHP中使用jwt还是非常顺利简单的,不过在laravel上却花费了一番力气才整理完毕。
1composer安装
$ composer require tymon/jwt-auth 1.0.0-rc.1
2配置
- 发布配置文件
# 这条命令会在 config 下增加一个 jwt.php 的配置文件
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
- 生成加密密钥
# 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar
$ php artisan jwt:secret
-
创建权限管理模型
laravel对于权限等等模块都有提供相应功能,这也是我在使用时候比较费解的问题,例如框架内部集成权限是使用User模型,其实根据业务不同肯定需要改动,正常情况我更喜欢自己去实现,毕竟文档中并没有详细的讲解其提供模块的实现,使用过程中花费时间去摸索也不容易啊。
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\MustVerifyEmail;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class Admin extends BaseModel implements
AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract,
JWTSubject
{
use Notifiable, Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail;
protected $hidden = ['password'];
protected $fillable = ['name', 'password'];
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
public function roles() {
return $this->belongsToMany(Role::class);
}
}
- 注册两个 Facade
这两个 Facade 并不是必须的,但是使用它们会给你的代码编写带来一点便利。
# ./config/app.php
'aliases' => [
...
// 添加以下两行
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
],
- 修改 auth.php
#./config/auth.php
//此处为我项目的配置
'guards' => [
'web' => [
'driver' => 'jwt',
'provider' => 'admins',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\Models\User::class
],
'admins' => [
'driver' => 'eloquent',
'model' => \App\Models\Admin::class,
],
],
3注册登录实现
- 控制器、注册、登录
这里说一下laravel权限封装,没有暴露出具体实现,我也没探究式如何生成密码的,所以账号一定要先注册后再进行登录。
namespace App\Http\Controllers\Admin;
use App\Exceptions\AuthException;
use App\Exceptions\LoginException;
use App\Http\Controllers\Controller;
use App\Logic\AuthLogic;
use App\Models\Admin;
use App\Utils\Response;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Tymon\JWTAuth\Facades\JWTAuth;
class AuthController extends Controller
{
/**
* 后台管理员登录
* @param Request $request
* @return \Illuminate\Http\JsonResponse
* @throws AuthException
* @throws LoginException
*/
public function login(Request $request)
{
if (! $token = auth("web")->attempt([
"name" => $request->input("userName"),
"password" => $request->input("password")
])) {
throw new LoginException(['msg' => '用户名或密码错误']);
}
if (JWTAuth::user()->status === 0) {
throw new AuthException(['msg' => '用户暂无权限登录']);
}
//将token返回给前端
JWTAuth::user()->token = $token;
//存储用户相关权限
(new AuthLogic(JWTAuth::user()))->saveAuthoritiesToCache();
return Response::result(JWTAuth::user());
}
/**
* 后台管理员注册
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function register(Request $request)
{
$name = $request->userName;
$password = $request->password;
$user = Admin::create(['name' => $name, 'password' => Hash::make($password)]);
$user->token = JWTAuth::fromUser($user);
return Response::result($user);
}
}
- 定义路由
# ./routes/web.php
Route::middleware('validator:Auth')->namespace('Admin')->prefix('admin')->group(function () {
// 后台登录
Route::post('login', 'AuthController@login');
//后台注册
Route::post('register', 'AuthController@register');
});
4中间件校验token并获取用户信息、权限等
- 创建校验token中间件
namespace App\Http\Middleware;
use App\Exceptions\LoginException;
use App\Exceptions\TokenException;
use Closure;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Facades\JWTAuth;
class CheckToken
{
/**
* 校验登录JWT的token
* @param Request $request
* @param Closure $next
* @return mixed
* @throws LoginException
* @throws TokenException
*/
public function handle(Request $request, Closure $next)
{
try {
if (!$user = JWTAuth::parseToken()->authenticate()) {
throw new LoginException(['msg' => "未登录,请先登录"]);
}
//如果想向控制器里传入用户信息,将数据添加到$request里面
$request->attributes->add(["user" => $user]);//添加参数
return $next($request);
} catch (TokenExpiredException $e) {
throw new TokenException(['msg' => "token 过期"]);
} catch (TokenInvalidException $e) {
throw new TokenException(['msg' => "token 无效"]);
} catch (JWTException $e) {
throw new TokenException(['msg' => "缺少token"]);
}
}
}
- 创建校验token权限中间件
namespace App\Http\Middleware;
use App\Exceptions\AuthException;
use App\Logic\AuthLogic;
use Closure;
class CheckTokenAuth
{
//不需要经过该中间件校验的路由
protected $except = [
"admin/menus/getUserRoutes",
];
/**
* 校验登录用户token绑定的路由权限
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @throws AuthException
*/
public function handle($request, Closure $next)
{
if (
!(new AuthLogic($request->user()))->checkAuth($request->route()->uri) &&
!in_array($request->route()->uri, $this->except)
) {
throw new AuthException();
}
return $next($request);
}
}
- 注册并合并上面创建的两个中间件
修改文件 ./app/Http/Kernel.php
protected $middlewareGroups = [
...
//校验token,并且校验访问路由权限
'api.auth' => [
'jwt.api.token',
'jwt.api.auth',
],
];
protected $routeMiddleware = [
//登录校验
'jwt.api.token' => \App\Http\Middleware\CheckToken::class,
//登录用户访问api路由权限校验
'jwt.api.auth' => \App\Http\Middleware\CheckTokenAuth::class,
];
- 路由配置
# ./routes/web.php
// Admin 模块
Route::middleware('api.auth')->namespace('Admin')->prefix('admin')->group(function () {
Route::middleware('validator:Admin')->prefix('/admins')->group(function () {
// 获取管理员列表
Route::get('getAdmins', 'AdminController@getAdmins');
})
});
- 控制器
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Logic\AdminLogic;
use App\Logic\AuthLogic;
use App\Models\Admin;
use App\Utils\Response;
use Illuminate\Http\Request;
class AdminController extends Controller
{
/**
* 获取管理员列表
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function getAdmins(Request $request) {
$admins = Admin::with("roles")->paginate($request->pageSize);
return Response::result($admins);
}
}