RBAC权限控制的实现原理与TP框架下实现源码

什么是RBAC?

基于角色的权限访问控制
(Role-Based Access Control)
比较好的解释:
http://blog.csdn.net/painsonline/article/details/7183613/

数据库结构分析:

4张表玩转权限控制
用户表
qing_admin

CREATE TABLE `qing_admin` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL DEFAULT '',
  `password` varchar(32) NOT NULL,
  `create_time` int(11) NOT NULL DEFAULT '0',
  `mobile` varchar(100) NOT NULL DEFAULT '0' COMMENT '权限字符串',
  `last_login_time` int(11) NOT NULL DEFAULT '0',
  `status` tinyint(1) DEFAULT '1',
  `email` varchar(20) NOT NULL,
  `group_id` varchar(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

用户组表(角色表)一个用户可以拥有多个角色,角色表
qing_auth_group

CREATE TABLE `qing_auth_group` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `title` char(100) NOT NULL DEFAULT '',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `rules` char(80) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

用户与角色关联表
qing_auth_group_access

CREATE TABLE `qing_auth_group_access` (
  `uid` mediumint(8) unsigned NOT NULL,
  `group_id` mediumint(8) unsigned NOT NULL,
  UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
  KEY `uid` (`uid`),
  KEY `group_id` (`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

权限表(更像是菜单表,无限分类技术)
qing_auth_rule

CREATE TABLE `qing_auth_rule` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `name` char(80) NOT NULL DEFAULT '' COMMENT '应用/控制器(大驼峰)/方法',
  `title` char(20) NOT NULL DEFAULT '' COMMENT '操作标题',
  `type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '放入的版块位置',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用(0禁用 1启用),默认启用',
  `condition` char(100) NOT NULL DEFAULT '',
  `parent_id` mediumint(9) NOT NULL DEFAULT '0' COMMENT '上级规则id 0表示顶级规则',
  `show` tinyint(1) NOT NULL DEFAULT '1' COMMENT '菜单是否显示(0不显示 1显示),默认显示',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;

权限控制的根本:

落实到代码上实际是http请求到对应页面(controller对应的action)的时候进行的拦截判断处理。用户登录时判断用户包含了哪些角色,该角色对应了哪些权限(也就是哪些控制器下的方法)并将其展示出来

thinkphp6.0.*框架实现源码

中间件做login登录拦截

前置中间-使用中间件做后台登录拦截,如果登录合法,就放行
1、判断当前有没有登录session
2、判断当前是不是登录页面,多次重定向的问题
3、后置中间件,弊端-在没有登录的状态下,就已经执行了代码,执行之后再跳转到登录

<?php
declare (strict_types = 1);

namespace app\dongadmin\middleware;

class Check
{
    /**
     * 处理请求
     *
     * @param \think\Request $request
     * @param \Closure       $next
     * @return Response
     */
    public function handle($request, \Closure $next)
    {
        //1、判断当前有没有登录session
        //2、判断当前是不是登录页面,多次重定向的问题

        //后置中间件,弊端-在没有登录的状态下,就已经执行了代码,执行之后再跳转到登录


        //未登录场景跳转到登录页
        if(empty(session('adminAccount')) && !preg_match('/login/',$request->pathinfo())){

            return  redirect((string) url('login/index'));
        }

        //使用中间件做后台登录拦截,如果登录合法,就放行 
        return $next($request);
    }
}

1、登录时判断是session是否存在adminAccount用户信息,存在则直接跳转到后台首页
2、提价登录信息逻辑post校验

<?php

namespace app\dongadmin\controller;
use think\facade\Db;
use app\BaseController;

class Login extends  BaseController
{
    //后台登录的逻辑
	public function index()
    {   
        //已登录直接跳转
        $account = session('adminAccount');
        if($account && $account['id']) {
            return redirect(url('index/index'));

        }
      
        if(request()->isPost()) {
        
            $data = input('post.');

            // 通过用户名 获取 用户相关信息

            $adminData = Db::name('admin')->where('username',$data['username'])->find();//一维数组
            

            if(!$adminData || $adminData['status'] !=1 ) {

                return alert('用户不存在,或者此用户未被审核通过','/dongadmin/login',5,3);

            }



            if(!captcha_check($data['verifycode'])) {
                // 校验失败
                return alert('验证码不正确','/dongadmin/login',5,3);
            }
    

            if($adminData['password'] !=password_salt($data['password'])) {
                return alert('密码不正确','/dongadmin/login',5,3);
            }
            //存入缓存
            session('adminAccount', $adminData);

            return alert('登录成功!','/dongadmin/index/index',6,3);


        }else {            
            return view();

        }

    }

   
}

thinkphp5.1框架实现源码

前端用户登录,post提交用户表单信息

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>登录页面</title>
</head>
<body>
<form action="{:url('Login/check')}" method="post">
    <p>用户名:</p>
    <p><input type="text" name="username"></p>
    <p><input type="submit" value="OK"></p>
</form>
</body>
</html>

check方法检查提交用户是否存在,获取查询数据库的用户信息将其保存在SESSION中跨页面访问用户信息并跳转到首页

<?php
namespace app\index\controller;

use think\Controller;
use think\facade\Session;
use think\facade\Request;
use think\Db;

class Login extends Controller
{
    public function index()
    {
        return $this->fetch();
    }
    public function check()
    {
        $username = Request::post('username');
        $data = Db::table('qing_admin')->where('username',$username)->find();
        //假设已经登录成功
        if($data){
            Session::set('username',$data['username']);
            Session::set('user_id',$data['id']);
            $this->success('登录成功','Index/index');
        }
        //清除session
        Session::clear();
        $this->error('该用户不存在,请重新输入');
    }


}

用于展示登录用户具有访问那些控制器下目录权限,需要注意的是在访问前都会去调用父类(RbacControll)里的初始化方法(initialize)查询该用户的权限具有访问哪些目录

<?php
namespace app\index\controller;

class Index extends RbacControll
{
    public function index()
    {
        echo "首页,用于展示登录用户具有访问那些控制器下目录权限";
    }

    public function select()
    {
        echo "查询";
    }
    public function delete()
    {
        echo "删除";
    }
    public function update()
    {
        echo "修改";
    }
    public function add()
    {
        echo "增加";
    }
}

这是每个后端控制器需要继承的父类,用于用户访问控制器下的方法前检查用户是否具有访问权限

<?php
namespace app\index\controller;
use think\Controller;
use think\facade\Session;
use think\facade\Request;
use think\Db;

//用于把控权限操作
class RbacControll extends Controller
{
    //用于继承该类的每个方法执行前都会先执行该初始化方法
    public function initialize()
    {
        //获取访问的控制器(Controll)与方法名(Action)用于判断访问的用户是否具有访问该控制器(Controll)下方法(Action)的权限
        $controller = Request::instance()->controller();
        $action = Request::instance()->module();

        $user_id = Session::get('user_id');
        $rules = Db::table('qing_auth_group')->leftJoin ('qing_auth_group_access','qing_auth_group.id = qing_auth_group_access.group_id')->where('qing_auth_group_access.uid',$user_id)->field('rules')->find();
        $data = Db::table('qing_auth_rule')->where('id','IN',$rules['rules'])->select();
        //查询用户具有访问的目录   子查询,使用到闭包传参
       /* $data = Db::table('qing_auth_rule')->where('id','IN', function ($query) use ($user_id){$query->table('qing_auth_group')->leftJoin ('qing_auth_group_access','qing_auth_group.id = qing_auth_group_access.group_id')->where('qing_auth_group_access.uid',$user_id)->field('rules');})->select()->getLastSql();*/
        if (!$data) {
            $this->error('无权操作', 'login/index');
        }
        dump($data);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值