业务系统权限管理升级说明

概述

权限限制统一由权限中心控制并且办法token和refresh_token;
统一授权中心地址:http://crm.v2.zbing.net/

token

有效期:待商定

token用来校验身份,可以解析出一下内容,其中 platform_uid 对应 业务系统的 dd_user_id及钉钉用户ID;

['userId' => $employee->id,'platform'=>$platform,'platform_uid'=>$platform_uid, 'userName' => $employee->username, 'cacheKey' => $cacheKey]
refresh_token

refresh_token 用来续期 token

如果获取token

业务系统前端登录已改造成跳转授权中心登录页面进行统一登录,登录成功后会返回业务系统。并携带 refresh_token,进入业务系统并且是用refresh_token获取完整token信息

一下是关于登录获取token的大体改造内容:

业务系统前端

文件:src/permission.js
改造内容:判断用户是否登录根据是否能够拿到token数据,如果没有则跳转到登录页面,其中的getToken方法导入文件是

// 文件位置 /home/baozi/yeziworkplace/yezi_front/src/utils/yzAuth.js
import { getToken } from '@/utils/yzAuth'

关键代码:

/**
 * 统一处理跳转登录
 */
function redirectToLogin(to) {
  const login_url = process.env.VUE_APP_PRIVATE_LOGIN_URL   // 环境变量中设置了统一授权中心的跳转地址
  const self_domain = window.location.origin
  const yz_rdn_url = self_domain + "/#" + to.path
  const yz_redirect_url = encodeURIComponent(yz_rdn_url)

  const yz_platform_id = process.env.VUE_APP_PLATFORM_ID || '1' //环境变量中获取VUE_APP_PLATFORM_ID值。默认是1 是必填项

  window.location.href = login_url + '?yzplatform=' + yz_platform_id + '&yz_redirect=' + yz_redirect_url
  NProgress.done()
}

登录成功后跳回业务系统后的处理关键代码,跳转后会携带authToken参数,其实就是refresh_token

  // -------------------------------
  // ① 情况一:URL 带 authToken(钉钉登录 / 单点登录)
  // -------------------------------
  if (to.params.authToken || to.query.authToken) {

    const authToken = to.params.authToken || to.query.authToken

    // 存入 Vuex & localStorage
    store.commit('yzAuth/SET_REFRESH_TOKEN', authToken)

    // 使用 authToken 从后台换 refreshToken + 有效期
    try {
      const newTokenData = await store.dispatch('yzAuth/refreshToken')

      if (!newTokenData) {
        console.error('authToken 无效,跳转登录')
        return redirectToLogin(to)
      }
    } catch (e) {
      console.error('authToken 刷新失败:', e)
      return redirectToLogin(to)
    }

    await store.dispatch('menus/initMenus')

    // 清理 URL 中的 authToken,不留脏参数
    return next({
      path: to.path,
      query: Object.assign({}, to.query, { authToken: undefined }),
      replace: true
    })
  }

后台

环境变量新增

# Jwt 密钥
JWT_AUTH_NAME=Authorization
JWT_ACCESS_TOKEN_KEY = {找bao获取}
JWT_REFRESH_TOKEN_KEY = {找bao获取}
JWT_ACCESS_EXPIRE = 1
JWT_REFRESH_EXPIRE = 72

# 权限校验的AppId
AUTH_APP_ID=1b62fff912a632c700e7771b9925722a
AUTH_PLATFORM_ID = 1

1.关于身份的识别

安装了 composer require firebase/php-jwt库 ,新增了 app/Services/JwtService.php文件来解密前端传递的token数据
改造了 app\http\Controller\Controller.php 中的 getUser方法,原来是直接根据传递的dd_userid获取用户信息,再造后是根据token解析出来的dd_userid

        $auth_token = $xtoken ?? \Illuminate\Support\Facades\Request::header('x-token');
        if (! $auth_token) {
            throw new \Error("请重新登录!1001",BaseConstant::NO_LOGIN);
        }

        try { 
            $jwtService = new \App\Services\JwtService();
            $tokenData = $jwtService->verifyAccessToken($auth_token);
            if (! $tokenData) {
                throw new \Error("身份认证错误!1002",BaseConstant::NO_LOGIN);
            }
        } catch (\Exception $e) {
            throw new \Error("获取用户数据失败!1003",BaseConstant::NO_LOGIN);
        }

        $x_token = $tokenData['data']->platform_uid;

2.关于权限节点的拦截

添加全局的中间件拦截器 app/Http/Middleware/YzAuthMiddleware.php,在Kernel.php进行注册。【后期还可考虑不全局放在router配置中】

namespace App\Http\Middleware;

use Closure;
use App\Http\BaseConstant;
use App\Services\YzAuthService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use \Illuminate\Support\Facades\Log;

class YzAuthMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        // 获取用户访问的资源路径
        $resource = $request->path();
        // 获取用户访问方式,是get还是post
        $method = strtolower($request->method());
        $whiteList = config('white_list');

        // 判断是否在白名单中
        foreach ($whiteList as $item) {
            if (strtolower($item[0]) == $method && $item[1] == $resource) {
                return $next($request);
            }
        }

        // 不在白名单,进行权限验证
        $auth_token = $xtoken ?? \Illuminate\Support\Facades\Request::header('x-token');
        if (! $auth_token) {
            return $next($request);
        }

        $jwtService = new \App\Services\JwtService();
        $tokenData = $jwtService->verifyAccessToken($auth_token);
        if ($tokenData && isset($tokenData['data']) && $tokenData['data']->userId>0) {
            $permissionsData = Redis::get('permissions:' . $tokenData['data']->platform_uid);

            if(!$permissionsData){
                // 根据 tokenData 设置用户信息,并且获取用户所有的权限数据
                $yzAuthService = new YzAuthService();
                $user = $yzAuthService->getUserPermissions($tokenData['data']->userId);

                Redis::set('permissions:' . $tokenData['data']->platform_uid, json_encode($user),'EX', 3600);

            }else{
                $permissionsData = json_decode($permissionsData, true);
            }
            foreach ($permissionsData['api'] as $perm) {
                if (isset($perm['path']) && isset($perm['method'])) {
                    if ($perm['path'] == $resource && strtolower($perm['method']) == $method) {
                        // 用户有权限访问该资源
                        return $next($request);
                    }
                }
            }

           return response()->json(['code' => BaseConstant::RETURN_FAIL_NORMAL, 'message' =>'暂无该资源权限访问该资源!']);
        }

        return $next($request);
    }
}

同时支持白名单配置 config/white_list.php ,在白名单中的不进行权限拦截和校验。

<?php
return [
    // 白名单列表
    ['get','api/refresh_token'],
    // ['get','api/dingtalk/get_user_info']
];

3.关于人员查看数据范围的控制改造

在文件 app/Services/YzAuthService.php 中有一下两个函数来生成查询sql的 whereIn(dd_dept_id,$dd_dept_list) 赋值

/**
     * 获取用户的数据操作范围
     * @param \Illuminate\Database\Eloquent\Builder $query  当前的查询构建器
     * @param string $field_name 用户ID字段名
     * @param string $dept_field_name 部门ID字段名
     * @param string $type 操作类型('read' 或 'write')
     * @param string $alias 查询表别名
     * @param \Closure|null $callback 自定义查询逻辑闭包
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function getUserScopeSql(&$query, $field_name = 'dd_user_id', $dept_field_name = 'dd_dept_id', $type = 'read', $alias = '')
    { 
        // 获取auth_token
        $auth_token = $xtoken ?? \Illuminate\Support\Facades\Request::header('x-token');
        if (!$auth_token) {
            throw new \Error($responseBody['message'] ?? '构造用户数据操作范围失败');
        }

        // 验证token
        $jwtService = new \App\Services\JwtService();
        $tokenData = $jwtService->verifyAccessToken($auth_token);

        // 从Redis获取权限数据
        $permissionsData = Redis::get('permissions:' . $tokenData['data']->platform_uid);
        if (!$permissionsData) { 
            throw new \Error($responseBody['message'] ?? '构造用户数据操作范围失败222');
        }

        $permissionsData = json_decode($permissionsData, true);

        // 默认的权限查询逻辑
        $data_scope = $permissionsData['scope_type'] ?? '1';

        if ($data_scope === '1') {
            $query->where(($alias ? $alias . '.' : '') . $field_name, $tokenData['data']->platform_uid);
        } elseif (in_array($data_scope, [2, 3, 4])) {
            $query->where(function ($_q) use ($field_name, $dept_field_name, $alias, $permissionsData, $tokenData, $type) {
                if ($type === 'read') {
                    $_q->whereIn(($alias ? $alias . '.' : '') . $dept_field_name, $permissionsData['platforms_read_scope'] ?? [])
                        ->orWhere(($alias ? $alias . '.' : '') . $field_name, $tokenData['data']->platform_uid);
                } else {
                    $_q->whereIn(($alias ? $alias . '.' : '') . $dept_field_name, $permissionsData['platforms_write_scope'] ?? [])
                        ->orWhere(($alias ? $alias . '.' : '') . $field_name, $tokenData['data']->platform_uid);
                }
            });
        }

        return $query;
    }

    /**
     * 获取用户的数据操作范围
     * @param \Illuminate\Database\Eloquent\Builder $query  当前的查询构建器
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function getUserScopeSqlByFunc(&$query,$callback)
    { 
        // 获取auth_token
        $auth_token = $xtoken ?? \Illuminate\Support\Facades\Request::header('x-token');
        if (!$auth_token) {
            throw new \Error($responseBody['message'] ?? '构造用户数据操作范围失败');
        }

        // 验证token
        $jwtService = new \App\Services\JwtService();
        $tokenData = $jwtService->verifyAccessToken($auth_token);

        // 从Redis获取权限数据
        $permissionsData = Redis::get('permissions:' . $tokenData['data']->platform_uid);
        if (!$permissionsData) { 
            throw new \Error($responseBody['message'] ?? '构造用户数据操作范围失败222');
        }

        $permissionsData = json_decode($permissionsData, true);

        // 执行用户自定义查询逻辑
        return $callback($query, $permissionsData, $tokenData);
    }
使用方法说明

情况一,逻辑简单

以CustomerController.php获取意向客户列表举例
原来代码使用if-else 判断role角色获取对应的人员拥有的dept_list数据,改版后通过 权限系统直接获取人员被赋值的dept_list

原代码

$dept_list = [];
            if (! CommonController::ifPermissions($user['roles'], 'customer') && ! ($user['admin'] && $user['admin'] == true)) {
                return response()->json(['code'=>BaseConstant::NO_PERMISSIONS, 'message'=>'无权限']);
            } else {
                if (in_array($user['userid'], config('dingtalk.customer_admin'))) {

                        $dept_list = CompanySignInfo::query()->pluck('dd_dept_id')->toArray();
                        $listQuery->where(function($query) use($dept_list, $user) {
                            $query->whereIn('crm_customer.dd_dept_id', $dept_list)->orWhere('crm_customer.dd_user_id', $user['userid']);
                        });

                } elseif (in_array('admin', $user['roles'])) {

                    $dept_list = User::getUserManageDepartmentList($user['userid']);#var_dump($dept_list);
                    $listQuery->where(function($query) use($dept_list, $user) {
                        $query->whereIn('crm_customer.dd_dept_id', $dept_list)->orWhere('crm_customer.dd_user_id', $user['userid']);
                    });
                } elseif ($user['roles'] == ['user'] || in_array('fts', $user['roles']) || in_array('auditor', $user['roles'])) {

                    $dept_list = UserCompany::getUserCompanys($user['userid']);
                    if (isset($queryParam['status']) && $queryParam['status'] == BaseConstant::CUSTOMER_STATUS['PUBLIC']) {

                        if (count($dept_list)) {
                            $listQuery->whereIn('crm_customer.dd_dept_id', $dept_list);//todo 获取用户是哪个分公司的
                        } else {
                            throw new \Error("用户没有设置部门,请联系管理员");
                        }
                    } else {
                        $listQuery->where('crm_customer.dd_user_id', $user['userid']);
                    }

                } elseif (in_array('accounting_manager', $user['roles'])) {
                    $dept_list = User::getUserManageDepartmentList($user['userid']);#var_dump($dept_list);
//                    var_dump($dept_list);
                    $listQuery->where(function($query) use($dept_list, $user) {
                        $query->whereIn('crm_customer.dd_dept_id', $dept_list)->orWhere('crm_customer.dd_user_id', $user['userid']);
                    });

//                } elseif (in_array('outwork_manager', $user['roles']) && ! in_array('manager', $user['roles'])) {
                } elseif (in_array('area_manager', $user['roles'])) {
                    $dept_list = User::getUserManageDepartmentList($user['userid']);#var_dump($dept_list);
                    $listQuery->where(function($query) use($dept_list, $user) {
                        $query->whereIn('crm_customer.dd_dept_id', $dept_list)->orWhere('crm_customer.dd_user_id', $user['userid']);
                    });
                } elseif (in_array('manager', $user['roles'])) {
                    if (in_array($user['userid'], ['1670909952913521', '16926732771311962'])) {
                        $dept_list = UserCompany::getUserCompanys('16484309558114235');
                    } else {
                        $dept_list = User::getUserManageDepartmentList($user['userid']);#var_dump($dept_list);
                    }
                    $listQuery->where(function($query) use($dept_list, $user) {
                        $query->whereIn('crm_customer.dd_dept_id', $dept_list)
                            ->orWhere('crm_customer.dd_user_id', $user['userid']);
                    });
                } elseif (in_array('area_personnel', $user['roles'])) {
                    $dept_list = User::getUserManageDepartmentList($user['userid']);#var_dump($dept_list);
                    $listQuery->where(function($query) use($dept_list, $user) {
                        $query->whereIn('crm_customer.dd_dept_id', $dept_list)->orWhere('crm_customer.dd_user_id', $user['userid']);
                    });
                } elseif (in_array('reserve_manager', $user['roles'])) {
                    $dept_list = User::getUserManageDepartmentList($user['userid']);#var_dump($dept_list);
                    $listQuery->where(function($query) use($dept_list, $user) {
                        $query->whereIn('crm_customer.dd_dept_id', $dept_list)->orWhere('crm_customer.dd_user_id', $user['userid']);
                    });
                } elseif(in_array($user['userid'], ['1655709808481485', '1653355988159449', '034239376726249358'])) {//侯俊萌/申振华/李旦旦 需要看销售意向,莘县王燕不需要看销售意向
                    $dept_list = User::getUserManageDepartmentList($user['userid']);#var_dump($dept_list);
                    $listQuery->where(function($query) use($dept_list, $user) {
                        $query->whereIn('crm_customer.dd_dept_id', $dept_list)->orWhere('crm_customer.dd_user_id', $user['userid']);
                    });
                } elseif (in_array('outwork_manager', $user['roles'])) {
                    if (isset($queryParam['status']) && $queryParam['status'] == BaseConstant::CUSTOMER_STATUS['PUBLIC']) {
                        $dept_list = User::getUserManageDepartmentList($user['userid']);
                        if (count($dept_list)) {
                            $listQuery->whereIn('crm_customer.dd_dept_id', $dept_list);//todo 获取用户是哪个分公司的
                        } else {
                            throw new \Error("用户没有设置部门,请联系管理员");
                        }
                    } else {
                        $dept_list = User::getUserManageDepartmentList($user['userid']);
                        $kj_users = User::query()
                            ->leftJoin('crm_user_company_relation', 'crm_users.userid', '=', 'crm_user_company_relation.dd_user_id')
                            ->whereIn('crm_user_company_relation.dd_dept_id', $dept_list)
                            ->whereIn('crm_users.position_title', config('dingtalk.accounting_position'))
                            ->pluck('crm_users.userid')->toArray();
                        $kj_users[] = $user['userid'];
                        $listQuery->whereIn('dd_user_id', $kj_users);
                    }
                } elseif (in_array('m0_manager', $user['roles'])) {
                    if (isset($queryParam['status']) && $queryParam['status'] == BaseConstant::CUSTOMER_STATUS['PUBLIC']) {
                        $dept_list = UserCompany::getUserCompanys($user['userid']);
                        if (count($dept_list)) {
                            $listQuery->whereIn('crm_customer.dd_dept_id', $dept_list);//todo 获取用户是哪个分公司的
                        } else {
                            throw new \Error("用户没有设置部门,请联系管理员");
                        }
                    } else {
                        $dept_list = UserCompany::getUserCompanys($user['userid']);
                        $sale_users = User::query()
                            ->leftJoin('crm_user_company_relation', 'crm_users.userid', '=', 'crm_user_company_relation.dd_user_id')
                            ->whereIn('crm_user_company_relation.dd_dept_id', $dept_list)
                            ->whereIn('crm_users.position_title', config('dingtalk.sale_position'))
                            ->pluck('crm_users.userid')->toArray();
                        $sale_users[] = $user['userid'];
                        $listQuery->whereIn('dd_user_id', $sale_users);
                    }
                } elseif (in_array('gc_outwork_manager', $user['roles'])) {

                    $dept_list = UserCompany::getUserCompanys($user['userid']);
                    if (isset($queryParam['status']) && $queryParam['status'] == BaseConstant::CUSTOMER_STATUS['PUBLIC']) {

                        if (count($dept_list)) {
                            $listQuery->whereIn('crm_customer.dd_dept_id', $dept_list);//todo 获取用户是哪个分公司的
                        } else {
                            throw new \Error("用户没有设置部门,请联系管理员");
                        }
                    } else {
                        $listQuery->where('crm_customer.dd_user_id', $user['userid']);
                    }

                } else {
                    $listQuery->where('crm_customer.dd_user_id', $user['userid']);
                }
            }

升级后代码

$yzAuthService = new \App\Services\YzAuthService();
$listQuery = $yzAuthService->getUserScopeSql($listQuery,'crm_customer.dd_user_id','crm_customer.dd_dept_id','read');

将升级后代码替换以上就代码即可,新代码getUserScopeSql方法返回一个 $listQuery->whereIn(‘crm_customer.dd_dept_id’, $dept_list);或者 $listQuery->where(‘crm_customer.dd_user_id’, $user[‘userid’])对象其中$dept_list根据权限系统中赋值

情况二,以线索列表举例
原代码

if (!CommonController::ifPermissions($user['roles'], 'customer') && !($user['admin'] && $user['admin'] == true)) {
                return response()->json(['code' => BaseConstant::NO_PERMISSIONS, 'message' => '无权限']);
            } else {
                if ($user['roles'] == ['user']) {
//                    $user_company_dept_id = Department::getDepartmentCompany($user['dept_id_list'][0]);
//                    $dept_list = [$user_company_dept_id];
                    $dept_list = UserCompany::getUserCompanys($user['userid']);
                    if (isset($queryParam['status']) && $queryParam['status'] == BaseConstant::CUSTOMER_STATUS['PUBLIC']) {

                        if (count($user['dept_id_list'])) {
                            $listQuery->whereIn('crm_lead.dd_dept_id', $dept_list);
                        } else {
                            throw new \Error("用户没有设置部门,请联系管理员");
                        }
                    } else {
                        $listQuery->where('crm_lead.dd_user_id', $user['userid']);
                    }
                } else {
                    $dept_list = User::getUserManageDepartmentList($user['userid']);
                    $listQuery->where(function ($query) use ($dept_list, $user) {
                        $query->whereIn('crm_lead.dd_dept_id', $dept_list);
                    });
                }
            }

升级后代码

$yzAuthService = new \App\Services\YzAuthService();
$listQuery = $yzAuthService->getUserScopeSqlByFunc(
    $listQuery, 
    function($query, $permissionsData, $tokenData)use($queryParam) {

        // 1.仅看自己 2.本部门 3.本部门及以下部门,4 指定部门 5.所有部门
        $data_scope = $permissionsData['scope_type'] ?? '1';

        if($data_scope === '1'){
            if (isset($queryParam['status']) && $queryParam['status'] == BaseConstant::CUSTOMER_STATUS['PUBLIC']) {
                $query->whereIn('crm_lead.dd_dept_id', $permissionsData['platforms_read_scope']);
            }else {
                $query->where('crm_lead.dd_user_id', $tokenData['data']->platform_uid);
            }
        }elseif(in_array($data_scope, [2,3,4])) {
            $query->where(function($_q) use( $permissionsData,$tokenData) {
                $_q->whereIn('crm_lead.dd_dept_id',  $permissionsData['platforms_read_scope'] ?? [])
                    ->orWhere('crm_lead.dd_user_id',$tokenData['data']->platform_uid);
            });

在回调函数中 会携带两个参数 $permissionsData 用户权限数据明细, $tokenData可以根据 $tokenData[‘data’]->platform_uid获取当前用户dd_userid

权限明细$permissionsData存储在redis中,数据结构如下

其中 user_id,user_name,roles 是权限系统的id和角色,目前可以忽略。

api 对应后台的路由节点权限
pages 对应前端的菜单和页面权限
btn 对应前端页面的按钮级别权限

**** 比较重要的有 scope_type 和 platforms_read_scope”,platforms_write_scope
其中scope_type值 1.仅看自己 2.本部门 3.本部门及以下部门,4 指定部门 5.所有部门,当为5是表示不限制,返回的platforms_read_scope和platforms_write_scope是空。
platforms_read_scope 是用户有读权限的 dd_dept_list
platforms_write_scope 是用户有写权限的 dd_dept_list

{
  "user_id": 45,
  "user_name": "张冰",
  "roles": [
    {
      "role_id": 1,
      "role_name": "超级管理员"
    }
  ],
  "api": [
    {
      "title": "公共方法",
      "method": "GET",
      "path": "api/dingtalk/get_user_info",
      "tag_name": "公共方法"
    },
    {
      "title": "获取消息列表",
      "method": "GET",
      "path": "api/dingtalk/get_notices_list",
      "tag_name": "公共方法"
    },
    {
      "title": "获取部门列表树",
      "method": "GET",
      "path": "api/dingtalk/task/get_dept_tree",
      "tag_name": "公共方法"
    },
    {
      "title": "获取新面板数据",
      "method": "POST",
      "path": "api/dingtalk/newdashboard",
      "tag_name": "公共方法"
    },
    {
      "title": "获取未完成任务列表",
      "method": "GET",
      "path": "api/dingtalk/task/un_done_count",
      "tag_name": "公共方法"
    },
    {
      "title": "获取新面板销售过程两数据",
      "method": "GET",
      "path": "api/dingtalk/dashboard/get_sale_info_day",
      "tag_name": "公共方法"
    },
    {
      "title": "recruit_amount_add_day",
      "method": "GET",
      "path": "api/dingtalk/recruit/recruit_amount_add_day",
      "tag_name": "公共方法"
    },
    {
      "title": "获取合同部门列表",
      "method": "GET",
      "path": "api/dingtalk/department/get_contract_department_list",
      "tag_name": "公共方法"
    },
    {
      "title": "客户列表",
      "method": "GET",
      "path": "api/dingtalk/customer/list",
      "tag_name": "客户管理"
    },
    {
      "title": "电话本列表",
      "method": "GET",
      "path": "api/dingtalk/customer_lead/list",
      "tag_name": "客户管理"
    },
    {
      "title": "个人简介",
      "method": "GET",
      "path": "api/dingtalk/user/get_user_profile",
      "tag_name": "公共方法"
    },
    {
      "title": "企业库查询条件",
      "method": "GET",
      "path": "api/dingtalk/enterprise_info_db/base_info",
      "tag_name": "客户管理"
    },
    {
      "title": "找资源列表",
      "method": "GET",
      "path": "api/dingtalk/enterprise_info_db/enterprise_info_list",
      "tag_name": "客户管理"
    },
    {
      "title": "获取人员目标数据",
      "method": "GET",
      "path": "api/dingtalk/usertarget/get_company_user_target_list",
      "tag_name": "公共方法"
    },
    {
      "title": "长时间未跟进客户",
      "method": "GET",
      "path": "api/dingtalk/get_follow_cold",
      "tag_name": "新工作台"
    },
    {
      "title": "获取城市管理人日报配置",
      "method": "GET",
      "path": "api/dingtalk/magager_ribao/getBaseManagerRibaoConfig",
      "tag_name": "新工作台"
    }
  ],
  "pages": [
    {
      "id": 2,
      "title": "客户",
      "en_title": "view",
      "locale": "menus.view",
      "parent_id": 24,
      "name": "menu_customer",
      "method": "POST",
      "path": "/customer",
      "icon": null,
      "type": 1,
      "sort_order": 100,
      "tag_name": "意向客户管理"
    },
    {
      "id": 3,
      "title": "意向客户",
      "en_title": "22",
      "locale": "23",
      "parent_id": 2,
      "name": "menu_customer_list",
      "method": "GET",
      "path": "/customer/customer_list",
      "icon": "23",
      "type": 1,
      "sort_order": 100,
      "tag_name": "23"
    },
    {
      "id": 4,
      "title": "电话本",
      "en_title": "22",
      "locale": "323",
      "parent_id": 2,
      "name": "menu_customer_lead",
      "method": "GET",
      "path": "/customer/customer_lead",
      "icon": null,
      "type": 1,
      "sort_order": 100,
      "tag_name": "ass"
    },
    {
      "id": 5,
      "title": "找资源",
      "en_title": "222",
      "locale": "222",
      "parent_id": 2,
      "name": "menu_enterprise_info",
      "method": "GET",
      "path": "/customer/enterprise_info",
      "icon": null,
      "type": 1,
      "sort_order": 100,
      "tag_name": "ass"
    }
  ],
  "btn": [
    {
      "title": "【btn】新增意向客户按钮",
      "name": "yzbtn_customer_add",
      "method": "GET",
      "path": "yzbtn_customer_add",
      "icon": null,
      "sort_order": 100,
      "tag_name": null
    }
  ],
  "scope_type": 5,
  "scope_type_name": "所有部门",
  "read_scope": [],
  "write_scope": [],
  "platforms_read_scope": [],
  "platforms_write_scope": [],
  "status": 1
}

前台

1.新增了环境变量
# 统一授权登录地址 可以设置成 http://crm.v2.zbing.net/ 线上地址方便开发
VUE_APP_PRIVATE_LOGIN_URL =  http://localhost:8000/user/login
# 平台类型ID
VUE_APP_PLATFORM_ID = 1
# 是否后台权限控制菜单显示
VUE_APP_SHOW_MENU_BY_AUTH = 0
2.修改 main.js

再大约53行添加了代码,注册v-yzauth指令,用于控制页面中按钮级别的权限控制,

import yzauth from '@/directive/yzauth'
Vue.use(yzauth)

使用示例:
其中 v-yzauth后面的 yzbtn_customer_add 对应权限系统的中添加的按钮节点path值

<el-button style="margin-left: 10px" v-yzauth="'yzbtn_customer_add'" type="success" @click="handleCreate">
  新增客户
</el-button>

3. 前端存储token的代码 src/store/modules/yzAuth.js
4. 前端刷新token的关键代码

文件:src/utils/request.js

service.interceptors.request.use(
  async config => {
    // 1. 请求时,检查 token 是否需要刷新
    const newToken = await store.dispatch("yzAuth/ensureValidAccessToken")

    // 2. 获取当前有效 token
    const finalToken = newToken || getToken()

    if (finalToken) {
      config.headers['X-Token'] = finalToken
    }

    return config
  },
  error => {
    console.error("Request error:", error)
    return Promise.reject(error)
  }
)
5.关于业务前端菜单根据权限控制显示的代码

文件:src/store/modules/permission.js

// 根据后台权限数据过滤路由
function filterRoutesByAuth(routes, parentPath = '') {
  const result = []
  routes.forEach(route => {
    const tmp = { ...route }
    // 构造当前路由的完整路径
    const fullPath = parentPath + (tmp.path.startsWith('/') ? tmp.path : '/' + tmp.path)
    const menusData = store.state.menus.menusData

    // 判断当前路由是否在权限数据中
    if(menusData && menusData.length > 0){
      const menu = menusData.find(item => item.path == fullPath)
      // 判断menu 是否 是undefined,如果是,则不显示
      if(menu){
        // 如果有子路由,递归处理
        if (tmp.children) {
          tmp.children = filterRoutesByAuth(tmp.children, fullPath)
        }
        result.push(tmp)
      }
    }
  })
  return result
}
const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
        // 这里根据环境变量判断是否开启权限控制菜单的显示
        if(process.env.VUE_APP_SHOW_MENU_BY_AUTH == '1'){
          accessedRoutes = filterRoutesByAuth(asyncRoutes,'');
        }else{
          accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
        }
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

控制的部分延用了原来的逻辑,只控制了 /src/router/index.js中的 export const asyncRoutes 部分。

6.按钮控制使用 前面2中说的v-yzauth指令
<el-button style="margin-left: 10px" v-yzauth="'yzbtn_customer_add'" type="success" @click="handleCreate">
  新增客户
</el-button>