菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
2146
5

利用 PHP 反射机制 获取权限

原创
05/13 14:22
阅读数 2038

_之前实现了一个基于角色的权限控制,需要后台手动添加权限,当系统庞大的时候,一条条的加无疑是一场灾难...后来看到公司的权限系统利用php反射机制获取权限,感觉很不错,自己查了下php的反射机制,应用在了自己博客中。以下下代码环境为laravel5.4


一、数据库表结构

截个图吧...

\QQ截图20170804164032.png

pri_name:权限名

pri_desc:权限描述

module_name:模块名

controller:控制器名

action_name:方法名

二、一些约定

1.代码编写时需要以下的格式

  /**
     * @name 添加操作
     * @desc 添加操作
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function addOperate(Request $request)
    {
        if($this->user->checkUnique($request->input('adminname'))){
            return response()->json(msg('error','该管理员已存在!'));
        }
        if($this->user->saveUser($request)){
            return response()->json(msg('success','添加成功!'));
        }
        return response()->json(msg('error','添加失败!'));
    }

需要注释加上@name @desc,@name是必须的

2.还需要配置你需要控制的模块,在config/app.php 加上

/**
     * 自定义配置
     */

    'ACCESS_CHECK_MODULE' => env('ACCESS_CHECK_MODULE','admin'),

    'name_space' =>'App\Http\Controllers',

.env 文件写上哪些模块需要权限控制

ACCESS_CHECK_MODULE = 'Admin'

3.被控制的模块路由地址必须和控制器方法名吻合,比如:

 //角色
Route::any('/role/add','RoleController@add');
Route::post('/role/addOperate','RoleController@addOperate');
Route::get('/role/index','RoleController@index');
Route::get('/role/edit/{id}','RoleController@edit')->where('id', '[0-9]+');
Route::post('/role/editOperate','RoleController@editOperate');
Route::post('/role/delete','RoleController@delete');

三、实现PBAC类

这个类的功能只是获取并返回权限

代码如下:

 <?php
/**
 * @name 利用反射获取权限树(路由)
 * @author [ycp] <[820363773@qq.com]>
 * Author: ycp
 * Date: 2017/6/14
 * Time: 10:35
 * Created by PhpStorm.
 */

namespace App\Models;
use ReflectionClass;

class Rbac
{
    public function getAccess(){

        //1.获取所有控制器
        $modules = config('app.ACCESS_CHECK_MODULE');
        $modules = explode(',',$modules);
        $controllers =[];
        $pris = [];
        //获取基础控制器的方法
        $base_controller = config('app.name_space').'\\'.'Controller';
        $reflection = new ReflectionClass($base_controller);
        $action_base_names = $reflection->getMethods();
        $action_base_name=[];
        foreach ($action_base_names as $v){
            $action_base_name[]=$v->name;
        }
        //循环模块
        foreach ($modules as $mk=>$mv){
            $controllers = $this->getController($mv);
            if($controllers == null){
                continue;
            }
            //循环控制器
            foreach ($controllers as $con){
                $con_name = str_replace('Controller','',basename($con));
                $reflection2 = new ReflectionClass($con);
                $action_names = $reflection2->getMethods();
                //循环方法
                foreach ($action_names as $ak=>$av){
                    $av_real = $av->name;
                    $desc = $av->getDocComment();
                    //控制器名称
                    if (!preg_match('/@name\s+(\w+)/u', $desc, $catch)) continue;

                    $name = $catch[1];
                    //控制器描述
                    $description = preg_match('/@desc\s+(\w+)/u', $desc, $catch)
                        ? $catch[1]
                        : '';
                    if(in_array($av_real,$action_base_name) || $av_real == '__construct'){
                        continue;
                    }else{
                        $pris[] = [
                            'module_name' =>$mv,
                            'controller' =>$con_name,
                            'action_name' =>$av_real,
                            'pri_name' =>$name,
                            'pri_desc' =>$description
                        ];
                    }
                }
            }

        }
        return $pris;
    }
    /**
     * 获取所有控制器名称 
     * @param string $module
     * @return array
     */
    protected function getController($module){

        if(empty($module)){
            return null;
        }
        $module_path = app_path('Http\Controllers').'/'.$module;//模块路径 
        $module_path = str_replace('\\','/',$module_path);

        if(!is_dir($module_path)) {
            return null;
        };
        $module_path .= '/*.php';

        $ary_files = glob($module_path);
        $files= [];
        foreach ($ary_files as $file){
            if(is_dir($file) || basename($file,'.php') =='LoginController'){
                continue;
            }else{
                $files[]=config('app.name_space').'\\'.$module.'\\'.basename($file,'.php');
            }
        }
        return $files;
    }
}

四、添加权限

 /**
     * @name 系统权限添加入库
     * @desc 系统权限添加入库
     * @return mixed
     */
    public function refreshPri()
    {
        $rbac = new Rbac();
        $pris = $rbac->getAccess();
        $ids = [] ; //更新或者添加的ID
        foreach ($pris as $k=>$v){
            //添加或者更新已有权限
            $pri = $this->updateOrCreate([
                'module_name' =>$v['module_name'],
                'controller' =>$v['controller'],
                'action_name' =>$v['action_name']
            ],[
                'pri_name' =>$v['pri_name'],
                'pri_desc' =>$v['pri_desc']
            ]);
            $ids[] = $pri->id;
        }
        //去掉删除的权限
        $old_ids = json_decode($this->pluck('id'),true);
        $delete_ids = array_diff($old_ids,$ids);
        foreach ($delete_ids as $v){
            $this->destroy($v);
        }
        return true;
    }

到这里php处理得就差不多了,然后就是前段展示的问题

五、展示权限

截个图...

\QQ截图20170804163830.png

六、权限控制

权限检测都在中间件中进行,定义AdminNeedsPermission中间件

<?php

namespace App\Http\Middleware;

use Closure;

class AdminNeedsPermission
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {

        //公共包不做权限验证
        if($request->is('common/*')){
            return $next($request);
        }
       if(checkPri($request->path())){
            if(\Request::ajax()){
                return response()->json(msg('error','您没有权限访问!'));
            }
            return redirect('admin')->with(['SYS_INFO'=>'您没有权限访问!']);
        }
        return $next($request);
    }
}

checkPri方法:添加到辅助方法helper中


/**
 * 检测权限
 */
if ( ! function_exists('checkPri')){
    function checkPri($url){
        if($url == 'admin'){//首页不验证
            return false;
        }
        $pris = SC::getUserAccess();
        if(count($pris)>0){
            if(!is_array($url)){
                $url = explode('/',$url);
            }
            foreach ($pris as $pri){
                if($pri->admin_id ===1){
                    return false;
                }
                if(strtolower($url[0]) == strtolower($pri->module_name) && strtolower($url[1]) == strtolower($pri->controller) && strtolower($url[2]) == strtolower($pri->action_name)){
                    return false;
                }
            }
        }
        return true;
    }
}

总结:实现起来并不复杂,从此不必手动一个个添加权限了,写得有点啰嗦,其实主要的是实现这个PBAC类。

以上代码都在项目https://github.com/ycp19940225/blog可以找到

个人博客:http://yangcp.me
——END

发表评论

0/200
2146 点赞
5 评论
收藏
为你推荐 换一批