用户权限控制 ----- Auth认证(lumen篇)

流程大览

这里写图片描述

在lumen中使用auth认证

  1. 注册AuthServiceProvider服务提供中,框架已经注册好了,只需要解开bootstrap/app.phpAuthServiceProvider 的调用取消代码注释;
  2. 支持ORM,用于获取认证用户,解开$app->withEloquent(); 的调用注释,如果需要通过Auth::User 的方式获取认证用户,需要解开$app->withFacades() 的调用;
  3. 支持路由中间件,把 bootstrap/app.php 里对 $app->routeMiddleware() 的调用 「取消代码注释」,$app->routeMiddleware([
    'auth' => App\Http\Middleware\Authenticate::class,
    ]);
    「取消代码注释;
  4. 还是在bootstrap/app.php 添加自定义配置文件role.php定义 $app->configure('role');,用于路由权限规则定义。

流程说明

登录流程:用户登录 —> 登录验证成功—>生成用户token并保存在数据库 ;
除登录外的其他访问:在AuthServiceProvider 服务提供中验证token的有效性与时效性,验证通过返回认证用户信息,在auth中间件中获取当前访问URI,通过当前认证用户的信息从数据库获取当前用户所拥有的权限,通过权限列表(具体的权限和路由具有对应关系)与当前路由验证用户是否拥有访问权限。

数据表设计

  • 用户表:
 Schema::create('staff_users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name',30)->comment('用户名称');
            $table->string('alias',30)->nullable()->comment('用户别名');
            $table->char('pwd',32)->comment('密码');
            $table->enum('status',[0,1])->default(1)->comment('状态 0 禁用 1 启用');
            $table->string('phone',11)->comment('手机');
            $table->json('extend')->nullable()->comment('用户扩展信息,如登录token等');
            $table->softDeletes();
            $table->timestamps();
            $table->index('status');

        });
  • 权限表:
Schema::create('staff_roles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('rule_name',20)->comment('权限名称');
            $table->string('rule',20)->comment('具体权限');
            $table->enum('status',[1,0])->comment('状态 0 禁用 1,启用');
            $table->softDeletes();
            $table->timestamps();
            $table->index('status');
            $table->unique('rule');
        });

*其中rule字段就是记录具体的访问路由,比如一个获取订单列表的URI是order/list 就记录成rule.list‘;

  • 权限小组表
 Schema::create('staff_group', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name',30)->comment('组名');
            $table->string('desc','200')->comment('简单描述');
            $table->enum('status',[0,1])->defaule(1)->comment('状态,0 禁用  1 启用');
            $table->softDeletes();
            $table->timestamps();
        });

该表用于权限分组

  • 小组权限对应表
 Schema::create('staff_group_rules', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('rule_id')->unsigned();
            $table->integer('group_id')->unsigned();
            $table->timestamps();
            $table->foreign('rule_id')->references('id')->on('staff_roles')->onDelete('cascade')->onUpdate('cascade');
            $table->foreign('group_id')->references('id')->on('staff_group')->onDelete('cascade')->onUpdate('cascade');
        });

该表用于记录规则与规则组对应关系

  • 用户小组表
 Schema::create('staff_users_group', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->integer('group_id')->unsigned();
            $table->timestamps();
            $table->foreign('user_id')->references('id')->on('staff_users')->onDelete('cascade')->onUpdate('cascade');
            $table->foreign('group_id')->references('id')->on('staff_group')->onDelete('cascade')->onUpdate('cascade');
        });

该表用于记录用户所属权限小组

代码实现:

  1. 登录:
public function login(Request $request)
    {
        $account = $request->input('account', '');
        $pwd = $request->input('password', '');
        if (!$account || !$pwd) {
            return Code::_620();
        }
        //注入服务类获取单个用户信息
        $info = $this->accountService->getInfoByName($account);

        if (!$info || $info->password != Assistants::getPwd($pwd)) {
        //自定义的json返回方式
            return Code::_620();
        }
        //自定义的服务容器
        $helper = app('helper');
        //产生token的自定义函数
        $pro = createToken($info->id);
        //将token按时间加密
        $token = $helper->getToken($pro);
        /**
        以下是其他业务,代码到这里登录流程就差不多了,产生了token,更新数据库的原token,如果没有存入token。
        **/
        $seller = $info->seller()->where('status', SellerAssistants::ACTIVE_STATUS)->get()->toArray();
        if(!$seller){
            return Code::_500('该账号已被停用');
        }
        $auth = env('AUTH');
        $seller = array_map(function ($item) use ($auth) {
            if ($item['is_master']) {
                $item['permissions'] = $auth;
            }
            return ['store_id' => $item['seller_id'], 'auth' => $item['permissions']];
        }, $seller);

        $seller_ids = array_column($seller, 'store_id');
        $store = $this->accountService->getStore($seller_ids);
        $tempSeller = [];
        foreach ($seller as $val) {
            $tempSeller[$val['store_id']] = $val;
        }
        $returnData = ['token' => $token, 'name' => $info->name, 'store' => []];
        foreach ($store as $val) {
            if (isset($tempSeller[$val['id']])) {
                $temp = $tempSeller[$val['id']];
                $temp['store_name'] = $val['name'];
                $returnData['store'][] = $temp;
            }
        }
        //更新token
        $extend = json_decode($info->extend, true);
        $extend['token'] = strtoupper($pro);
        if (count($store) == 1) {
            $extend['lastStore'] = $store[0]['id'];
        }
        $info->extend = json_encode($extend, true);
        if (!$info->save()) {
            return Code::_620();
        }
        return Code::Y($returnData);
    }
    //以下为
  1. 生成时间加密的token:
//这里是对第一步的补充说明,分别是生产token,按时间加密,解密token的相关函数
//生产token
function createToken($uid = 0)
{
    $time = uniqid($uid);
    $rand = random_int(0, 1000);
    return md5(md5($time) . md5($rand));
}
//时间加密
function getToken($token='',$str=''){
        if(!$token){
            $token = strtoupper(env('ACCESS_KEY','MYNAMEDIAMONDS@A!KILL^&!MING@YIQ'));
        }
        $str = $str?$str:'MINGYI';
        $l=strlen($str);
        $l=$l>8?8:$l;
        $len = 0 - $l;
        $time=substr(date('Ymd'),$len);
        $b = $time;
        while($time !==''){
            $char = $time[0];
            $token = substr_replace($token,$str[0],$char,0);
            $time =substr_replace($time,'',0,1);
            $str =substr_replace($str,'',0,1);
        }
        if(strlen($l)<2){
            $l='A'.$l;
        }
        return strtoupper($l.$token.$b);
    }
    //解密获取原生token
   function getProToken($token=''){
        if(!$token){
            return $token;
        }
        $l = substr($token,0,2);
        if(intval($l) <1){
            $l = substr($l,-1);
        }
        $len= 0 - $l;
        if($len >= 0){
            return $token;
        }
        $token = substr($token,2);
        $time = substr($token,$len);
        $now = date('Ymd');
        $pre = substr_replace($now,$time,$len,$l);
        $str=[];
        while ($time !==''){
            $index= strlen($time) - 1;
            $s = $time[$index];
            array_unshift($str,$token[$s]);
            $token = substr_replace($token,'',$s,1);
            $time = substr_replace($time,'',$index,1);
        }
        $token =substr_replace($token,'',$len);
        return ['token'=>$token,'time'=>$pre,'str'=>join('',$str)];
    }
  1. AuthServiceProvider服务提供者token验证
/** AuthServiceProvider服务提供者位置: app/Providers/AuthServiceProvider.php
**/

//用户信息我是存在这个模型中的,默认是use的User
use App\Models\Assistants;
.........
  public function boot()
    {
        // Here you may define how you wish users to be authenticated for your Lumen
        // application. The callback which receives the incoming request instance
        // should return either a User instance or null. You're free to obtain
        // the User instance via an API token or any other method necessary.

        $this->app['auth']->viaRequest('api', function ($request) {
            if(env('AUTH_DEBUG',false)){   //调试模式不验证
                return Assistants::first();
            }
            //从消息头获取传入的token
            $token = $request->headers->get('token');
            if(!$token){
                die("token不能为空!");  
            }
            //解密原声token,获取token,时间,过期时间
            $sign = app('helper')->getProToken($token);
            $date = date("Ymd");
            $timeout = env('TOKEN_TIMEOUT',0);
            //验证token是否过期
            if ((!$timeout || ($date - (int)$sign['time']) < $timeout) && $sign['token']) {
            //token验证通过返回当前认证用户
                return Assistants::where('extend->token',$sign['token'])->first();
            }
            return null;
        });
    }
  1. auth中间件路由权限检测
/**
auth中间件的位置:app/Http/Middleware/Authenticate.php
**/
  /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  string|null $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if ($this->auth->guard($guard)->guest()) {
            return Code::_401();
        }
//获取认证用户信息$request->user()
        $userExtend = json_decode($request->user()->extend, true);
        if (!isset($userExtend['lastStore'])) {
            return Code::_401();
        }
        /** 获取权限begin
        以下代码根据设计的数据库,结合当前用户信息获取用户拥有的权限(或所在权限组拥有的权限,权限可以理解成拥有哪些路由)
        **/
        $sellerUser = SellerAssistants::where([['seller_id', $userExtend['lastStore']], ['assistant_id', $request->user()->id]])->first();
        if (!$sellerUser->is_master) {
            $permissions = explode(',', $sellerUser->permissions);
            $commonRole = config('role.common');
            $role = config('role.auth');
            $userRole = array_flip($permissions);
            $vaild = array_merge(array_flatten(array_intersect_key($role, $userRole)), $commonRole);
            //获取权限 end
            //获取当前URI
            $path = $request->path();
            //获取以“/”开始后面的路由,例如:http://www.xxx.com/order/list 就获取order.list
            $method = str_replace('/', '.', substr($path, 0, strpos($path, '/')));
            //判断当前处理过后的路由是否在所拥有的权限中
            if (!in_array($method, $vaild)) {
                return Code::_402();
            }
        }
        //验证通过
        return $next($request);
    }
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值