本项目使用DingoAPI作为路由,未安装请参考并进行安装配置:
https://learnku.com/docs/dingo-api/2.0.0
添加用户注册路由
routes/api.php
.
.
.
$api->version('v1', [
"prefix" => "api",
'namespace' => 'App\Http\Controllers\Api',
], function (routesr $api) {
// 用户注册
$api->post('users', 'UsersController@store')
->name('api.users.store');
.
.
.
php artisan make:controller Api/UsersController
php artisan make:request Api/UserRequest
修改文件
app/Http/Controllers/UsersController.php
.
.
.
class UsersController extends Controller
{
use ApiTraits;
public function store(UserRequest $request)
{
$user = User::create([
'name' => $request->username,
'password' => bcrypt($request->password),
]);
return $this->apiReturn($user,CodeEnum::INC_SUCCESS);
}
}
app/Http/Requests/Api/Request.php
namespace App\Http\Requests\Api;
use Dingo\Api\Http\FormRequest;
class UserRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|between:3,25|regex:/^[A-Za-z0-9\-\_]+$/|unique:users,name',
'password' => 'required|string|min:5',
];
}
public function messages()
{
return [
'username.required' => '用户名 必须',
'username.between' => '用户名 长度过短或过长',
'name.regex' => '用户名 只支持英文,数字,横杆和下划线',
'username.unique' => '用户名 已存在',
'password.required' => '密码 必须',
'password.min' => '密码 长度最小为5',
];
}
}
其中引用Trait类-ApiTraits,统一接口返回
CodeEnum统一存放枚举值
app/Traits/ApiTraits.php
namespace App\Traits;
/**
* Trait ApiTraits:api处理
* @package App\Traits
* @author zhaodayuan
* @date 2020/2/3
*/
Trait ApiTraits
{
public function apiReturn($data = [], $codeEnum)
{
return [
'status' => (int) $codeEnum[0],
'message' => (string) $codeEnum[1],
'data' => $data
];
}
}
app/Enum/CodeEnum.php
namespace App\Enum;
/**
* 响应码表
* 所有接口在使用务必在此定义
* 将所有的响应码中心化,同时也便于查询
* Class CodeEnum
* @package App\Enums
*/
class CodeEnum
{
const SUCCESS = [200, '操作成功'];
const INC_SUCCESS = [201, '新增成功'];
const FAIL = [999, '操作失败'];
const NON_EXISTENT = [500, '信息不存在'];
const DATA_EMPTY = [998, '信息为空'];
const PARAMS_MISS = [997, '参数缺失'];
const ERR_NAME_OR_PASSWORD = [996, '用户名或密码错误'];
const NOT_PERMISSION = [401, '权限不足'];
}
本项目使用JWT作为用户登陆凭证,如果没有了解过,请参考:
https://www.cnblogs.com/skaarl/p/9508628.html
首先来安装一下,Laravel 5.5 的适配版本为 1.0.0-rc.2
composer require tymon/jwt-auth:1.0.0-rc.2
安装完成后,我们需要设置一下 JWT 的 secret,这个 secret 很重要,用于最后的签名,更换这个 secret 会导致之前生成的所有 token 无效。
php artisan jwt:secret
可以看到在 .env 文件中,增加了一行 JWT_SECRET
修改 config/auth.php,将 api guard 的 driver 改为 jwt
config/auth.php
.
.
.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
.
.
.
修改 config/api.php,auth 中增加 JWT 相关的配置
config/api.php
.
.
.
'auth' => [
'jwt' => 'Dingo\Api\Auth\Provider\JWT',
],
.
.
.
user 模型需要继承 Tymon\JWTAuth\Contracts\JWTSubject
接口,并实现接口的两个方法 getJWTIdentifier () 和 getJWTCustomClaims ()
app\Model\User.php
namespace App\Model;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
protected $table = 'users';
public $timestamps = true;
protected $fillable = [
'name', 'password'
];
protected $hidden = [
'password', 'remember_token'
];
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
.
.
.
$api->version('v1', [
"prefix" => "api",
'namespace' => 'App\Http\Controllers\Api',
], function (routesr $api) {
// 获取token
$api->post('authorizations', 'AuthorizationsController@login')
->name('api.authorizations.login');
.
.
.
php artisan make:controller Api/AuthorizationsController
php artisan make:request Api/AuthorizationsRequest
修改app/Http/Requests/Api/AuthorizationsRequest.php
namespace App\Http\Requests\Api;
use Illuminate\Foundation\Http\FormRequest;
class AuthorizationRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'username' => 'required|string',
'password' => 'required|string|min:5',
];
}
public function messages()
{
return [
'username.required' => '用户名 必须',
'username.string' => '用户名 必须为字符串',
'password.required' => '密码 必须',
'username.string' => '密码 必须为字符串',
'password.min' => '密码 长度最小为5',
];
}
}
修改app/Http/Controller/Api/AuthorizationsController.php
namespace App\Http\Controllers\Api;
use App\Enum\CodeEnum;
use \App\Traits\ApiTraits;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\AuthorizationRequest;
class AuthorizationsController extends Controller
{
use ApiTraits;
public function login(AuthorizationRequest $request)
{
$credentials['name'] = $request->username;
$credentials['password'] = $request->password;
if (!$token = \Auth::guard('api')->attempt($credentials)) {
return $this->apiReturn('',CodeEnum::ERR_NAME_OR_PASSWORD);
}
return $this->apiReturn([
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => \Auth::guard('api')->factory()->getTTL() * 60
],CodeEnum::SUCCESS);
}
}
登录获取了 token 之后,第一件事就是需要换取用户信息,先来实现 获取登录用户信息
接口。
routes/api.php
$api->version('v1', [
"prefix" => "api",
'namespace' => 'App\Http\Controllers\Api',
], function (Router $api) {
.
.
.
// 需要 token 验证的接口
$api->group(['middleware' => 'auth:api'], function($api) {
// 当前登录用户信息
$api->get('user', 'UsersController@me')
->name('api.user.show');
.
.
.
app/Http/Controllers/Api/UsersController.php
.
.
.
public function me()
{
$user = \Auth::guard('api')->user();
$user->mate = [
'access_token' => \Auth::guard('api')->fromUser(\Auth::guard('api')->user()),
'token_type' => 'Bearer',
'expires_in' => \Auth::guard('api')->factory()->getTTL() * 60
];
return $this->apiReturn($user,CodeEnum::SUCCESS);
}
.
.
.
routes/api.php
$api->version('v1', [
"prefix" => "api",
'namespace' => 'App\Http\Controllers\Api',
], function (Router $api) {
.
.
.
// 需要 token 验证的接口
$api->group(['middleware' => 'auth:api'], function($api) {
// 编辑登录用户信息
$api->patch('user', 'UsersController@update')
->name('api.user.update');
.
.
.
修改 UserRequest
app/Http/Requests/Api/UserRequest.php
.
.
.
public function rules()
{
switch($this->method()) {
case 'POST':
return [
'name' => 'between:3,25|regex:/^[A-Za-z0-9\-\_]+$/|unique:users,name',
'password' => 'required|string|min:5',
];
break;
case 'PATCH':
$userId = \Auth::guard('api')->id();
return [
'name' => 'between:3,25|regex:/^[A-Za-z0-9\-\_]+$/|unique:users,name,' .$userId,
];
break;
}
}
.
.
.
app/Http/Controllers/Api/UserController.php
.
.
.
.
.
public function update(UserRequest $request)
{
$user = \Auth::guard('api')->user();
$attributes = $request->only(['name']);
$user->update($attributes);
return $this->apiReturn($user,CodeEnum::SUCCESS);
}
.
.
.
记得添加 Authorization
laravel_permission
安装包的安装composer require "spatie/laravel-permission:~2.7"
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"
这段命令生成迁移文件,然后执行 php artisan migrate
进行文件迁移(创建 5 张数据表:权限表,角色表,权限角色关系表,角色用户关系表,权限用户关系表)php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"
=> config/permisson.phpdatabase/migrations/2020_02_15_130314_create_permission_tables.php
可能名字会有些不同,跟当前日期有关,这些都没有关系
运行迁移文件
php artisan migrate
app/Model/User.php
.
.
.
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable implements JWTSubject
{
use HasRoles;
.
.
.
可以根据当前项目进行自主添加权限和角色
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class SeedRolesAndPermissionsData extends Migration
{
public function up()
{
// 清除缓存
app()['cache']->forget('spatie.permission.cache');
// 创建权限 Persisson::create(['name' => '权限名'])
Permission::create(['name' => 'material-management']);
// 创建角色 Role::create(['name' => '角色名'])
$founder = Role::create(['name' => 'Founder']); //站长
// 赋予权限 $角色->givePermessionTo('权限名')
$founder->givePermissionTo('material-management');
}
public function down()
{
// 清除缓存
app()['cache']->forget('spatie.permission.cache');
// 清空所有数据表数据
Model::unguard(); //记得解除模型保护
DB::table('role_has_permissions')->delete();
DB::table('model_has_roles')->delete();
DB::table('model_has_permissions')->delete();
DB::table('roles')->delete();
DB::table('permissions')->delete();
Model::reguard(); //最后重新开启模型保护
}
}
运行迁移文件
php artisan migrate
routes/api.php
.
.
.
// 需要 token 验证的接口
$api->group(['middleware' => 'auth:api'], function($api) {
// 当前登录用户信息
$api->get('user', 'UsersController@me')
->name('api.user.show');
// 编辑登录用户信息
$api->patch('user', 'UsersController@update')
->name('api.user.update');
// 图片资源
$api->post('images', 'ImagesController@store')
->name('api.images.store');
//角色权限
$api->group(['prefix' => 'role-permission'], function ($api){
// 所有权限列表
$api->get('list','RolePermissionController@list');
// 赋予角色权限
$api->post('givepermisstorole','RolePermissionController@givePermissionToRole');
// 取消角色权限
$api->post('revokepermisstorole','RolePermissionController@revokePermissionToRole');
// 获取当前角色权限
$api->post('rolehavepermisson','RolePermissionController@roleHavePermisson');
// 添加角色
$api->post('addrole','RolePermissionController@addRole');
});
.
.
.
namespace App\Http\Controllers\Api;
use App\Enum\CodeEnum;
use App\Traits\ApiTraits;
use Dingo\Api\Http\Request;
use Spatie\Permission\Models\Role;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Permission;
class RolePermissionController extends Controller
{
use ApiTraits;
/**
* function 添加角色
* describe 添加角色
* @param Request $request
* @param Role $role
* @return array
* @author ZhaoDaYuan
* 2020/2/16 下午9:40
*/
public function addRole(Request $request,Role $role)
{
$role_name = $request->all()['name'];
$result = $role::create(['name' => $role_name,'guard_name' => "web"]);
return $this->apiReturn($result,CodeEnum::INC_SUCCESS);
}
/**
* function 所有权限列表
* describe 所有权限列表
* @param Permission $permission
* @return array
* @author ZhaoDaYuan
* 2020/2/16 下午9:41
*/
public function list(Permission $permission)
{
return $this->apiReturn($permission::all(),CodeEnum::SUCCESS);
}
/**
* function 赋予角色权限
* describe 赋予角色权限
* @param Request $request
* @param Role $role
* @return array
* @author ZhaoDaYuan
* 2020/2/16 下午9:41
*/
public function givePermissionToRole(Request $request,Role $role)
{
$role_id = $request->all()['id'];
$permission = $request->all()['permission'];
$role = $role::findById($role_id);
$result = $role->givePermissionTo($permission);
return $this->apiReturn($result,CodeEnum::SUCCESS);
}
/**
* function 取消角色权限
* describe 取消角色权限
* @param Request $request
* @param Role $role
* @return array
* @author ZhaoDaYuan
* 2020/2/16 下午9:41
*/
public function revokePermissionToRole(Request $request,Role $role)
{
$role_id = $request->all()['id'];
$permission = $request->all()['permission'];
$role = $role::findById($role_id);
$result = $role->revokePermissionTo($permission);
return $this->apiReturn($result,CodeEnum::SUCCESS);
}
/**
* function 获取当前角色权限
* describe 获取当前角色权限
* @param Request $request
* @param Role $role
* @return array
* @author ZhaoDaYuan
* 2020/2/16 下午9:42
*/
public function roleHavePermisson(Request $request,Role $role)
{
$role_id = $request->all()['id'];
$role = $role::findById($role_id);
$permisson = $role->getAllPermissions();;
return $this->apiReturn($permisson,CodeEnum::SUCCESS);
}
}
自定义中间件
app/Http/Middleware/Permission.php
namespace App\Http\Middleware;
use Closure;
use App\Enum\CodeEnum;
use App\Traits\ApiTraits;
use Illuminate\Contracts\Auth\Access\Gate;
class Permission
{
use ApiTraits;
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// 获取当前路径中的模块部分
$path = $request->segments()[1];
// 获取当前用户信息
$user = \Auth::guard('api')->user();
// 对用户权限检测
$permisson = app(Gate::class)->forUser($user)->check($path);
// 用户检测通过可以继续路由
if ($permisson) {
return $next($request);
}else{
// 用户检测不通过则返回`权限不足`
return $this->apiReturn('',CodeEnum::NOT_PERMISSION);
}
}
}
注册中间件
app/Http/Kernel.php
protected $routeMiddleware = [
.
.
.
'permission' =>\App\Http\Middleware\Permission::class,
];
修改路由文件
routes/api.php
.
.
.
$api->group(['middleware' => 'permission'],function ($api) {
.
.
.
赋予角色权限
移除角色权限
\Auth::guard('api')->user()->getAllPermissions();
到此为止,已经完成了权限管理模块的所有代码!!!
由于现在一个项目都是由多个开发人员进行协作开发,包括前端和后端,所以
前端如何获得当前用户可查看的模块
修改用户获取个人信息模块
app/Http/Controllers/Api/UserController.php
public function me()
{
// 获取当前登陆用户信息
$user = \Auth::guard('api')->user();
// 获取当前用户所有权限
$models_permission = $user->getAllPermissions();
foreach ($models_permission as $key=>$value) {
$models_permission[$key]['cn_name'] = config("module.".$value['name']);
}
$user->mate = [
'access_token' => \Auth::guard('api')->fromUser(\Auth::guard('api')->user()),
'token_type' => 'Bearer',
'expires_in' => \Auth::guard('api')->factory()->getTTL() * 60
];
$user->permission = $models_permission;
unset($user['permissions'],$user['roles']);
return $this->apiReturn($user,CodeEnum::SUCCESS);
}
这样每当用户登陆后,将返回可以访问的模块,前端根据返回做相应的显示判断
后端人员增加模块,怎么自动将模块信息注册到数据库权限表
能够自动添加信息的方式有很多种,在这里最想想到使用的是定时任务
php artisan make:command AddModule
修改命令行文件
app/Console/Commands/AddModule.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Spatie\Permission\Contracts\Permission;
use Log;
class AddModule extends Command
{
protected $signature = 'p_o:add-module';
protected $description = '将所有模块注册到数据库';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$module = config('module');
$module_names = array_keys($module);
foreach ($module_names as $module_name)
{
/**
* @var $permission Permission
*/
$permission = app()->make(Permission::class);
$permission::findOrCreate($module_name,'web');
Log::info('向数据库添加模块:'.$module_name);
}
}
}
这个时候运行 php artisan p_o:add-module
居然显示
Command "p_o:add-module" is not defined.
O(∩_∩)O哈哈~ 这是自然,因为自定义的命令需要进行注册
app/Console/Commands/Kernel.php
.
.
.
protected $commands = [
\App\Console\Commands\AddModule::class,
];
.
.
.
此时已经可以使用命令了,不妨试一下,可以在日志文件中看见入下记录:
[2020-02-16 14:00:01] local.INFO: 向数据库添加模块:xxx
[2020-02-16 14:00:01] local.INFO: 向数据库添加模块:xxx
[2020-02-16 14:00:01] local.INFO: 向数据库添加模块:xxx
最后一步,开启定时任务,这里设置的是每隔30分钟调用一次
app/Console/Commands/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command('p_o:add-module')->everyThirtyMinutes();
}
配置Linux定时任务
crontab -e
#添加定时任务,/path/to/artisan为项目根目录
* * * * * php /path/to/artisan schedule:run
#保存退出
定时任务 https://learnku.com/laravel/t/1402/laravel-timing-task
L03 Laravel 教程 - 实战构架 API 服务器 ( Laravel 5.5 ) https://learnku.com/courses/laravel-advance-training/6.x
L02 Laravel 教程 - Web 开发实战进阶 ( Laravel 5.5 ) https://learnku.com/laravel/t/15264/the-summing-up-of-the-two-courses