zl程序教程

您现在的位置是:首页 >  Javascript

当前栏目

飞冰笔记1-实现权限管理

2023-04-18 14:47:13 时间

在使用飞冰框架过程中,有这么几点感触,首先飞冰是一个框架,是基于react技术,使用next组件和其他若干工具库搭建的一个框架,使用这个框架开发应用会大大缩短开发周期,举个形象,react相当于使用的是某一种砖头,ui组件是某种风格,而框架是毛坯房。

今天使用飞冰的权限管理,做一下笔记心得。

对于一个 Web 应用,权限管理是经常会涉及的需求之一,通常包含以下几种常见的权限管理类型:

  1. 页面权限:当用户访问某个没有权限的页面时跳转到无权限页面;
  2. 操作权限:页面中的某些按钮或组件针对无权限的用户直接隐藏;
  3. 接口权限:当用户通过操作调用没有权限的接口时跳转到无权限页面。

首先我们要初始化权限数据,大多数情况下权限管理通常需要从服务端获取权限数据,然后在前端通过权限对比以此控制页面、操作等等权限行为。在 icejs 框架中约定通过 getInitialData 从服务端异步获取初始化的权限数据,并且约定最终返回格式为 {auth: {[key: string]: boolean }} 的形式。

import { runApp, request, IAppConfig } from 'ice';

const appConfig: IAppConfig = {
  app: {
    getInitialData: async () => {
      // 模拟服务端返回的数据
      const data = await request('/api/auth');
      const { role, starPermission, followPermission } = data;

      // 约定权限必须返回一个 auth 对象
      // 返回的每个值对应一条权限
      return {
        auth: {
          admin: role === 'admin',
          guest: role === 'guest',
          starRepo: starPermission,
          followRepo: followPermission,
        },
      };
    },
  },
  auth: {
    // 可选的,设置无权限时的展示组件,默认为 null
    NoAuthFallback: <div>没有权限...</div>,
    // 或者传递一个函数组件
    // NoAuthFallback: () => <div>没有权限..</div>
  },
};

runApp(appConfig);

上面的代码配置在app.js中,也就是入口文件,每次刷新应用都会重新执行权限函数,权限刷新有两种方式:

1、一种是手动刷新页面。

2、一种是通过飞冰权限API函数来设置。

import React from 'react';
import { useAuth } from 'ice';

function Foo() {
  const [auth, setAuth] = useAuth();

  // 更新权限,与默认的 auth 数据进行合并
  function updateAuth() {
    setAuth({ starRepo: false, followRepo: false });
  }

  return (
    <>
      当前用户角色:
      <code>{JSON.stringify(auth)}</code>
      <button type="button" onClick={updateAuth}>
        更新权限
      </button>
    </>
  );
}

我们通过setAuth来动态更新了应用的权限设置,这里需要注意这个权限是全局的,一般什么时候会用到呢?一般是在登录组件中用到,我们在登录组件实现登录后,需要跳转到应用首页或者目标页面,但是ice通过history这个实例跳转页面,应用并不会刷新,即不会刷新浏览器,那么就会造成一个问题,我们明明登陆了,并且本地存储了token,但是全局的权限状态并没有更改,该怎么做呢,此时就需要在登录组件的登录异步函数验证的结尾调用更改权限的函数。

  const handleSubmit = (values, errors) => {
    if (errors) {
      // eslint-disable-next-line no-console
      console.log('errors', errors);
      return;
    }
    // eslint-disable-next-line no-console
    console.log('values:', values);
    localStorage.setItem('login', '1');
    setAuth({ starRepo: true, followRepo: true, admin: 'true' });
    Message.success('登录成功');
    history.push('/');
  };

这样虽然应用没有刷新但是权限状态发生了变化,这就给了我们一个启示,当我们设置全局状态的时候,一般需要一个接口函数能手动更新,页面每次刷新全局状态也能和后端同步。

接着看一下页面权限的设置,页面权限通常也称之为路由权限,如需对某些页面进行权限控制只需在页面组件的 pageConfig 中配置准入权限即可,只需要在routes.jsz中配置即可:

// src/routes.ts
import Home from '@/pages/Home';

export default [
  {
    path: '/home',
    component: Home,
+    // icejs 1.x 仅支持将 pageConfig 配置在对应的页面组件上,请参考「页面组件」章节
+    pageConfig: {
+      auth: ['admin'],
+    },
  },
];

export default routerConfig;

然后是操作权限,在某些场景下,如某个组件中要根据角色判断是否有操作权限,我们可以通过useAuthHooks 在组件中获取权限数据,同时也可以更新初始的权限数据。

import React from 'react';
import { useAuth } from 'ice';

function Foo() {
  const [auth] = useAuth();
  return (
    <>
      当前用户权限数据:
      <code>{JSON.stringify(auth)}</code>
    </>
  );
}

获取了用户的权限数据,就可以开发自定义权限组件,首先开发一个高阶组件:

import React from 'react';
import { useAuth } from 'ice';
import NoAuth from '@/components/NoAuth';

function Auth({ children, authKey, fallback }) {
  const [auth] = useAuth();
  // 判断是否有权限
  const hasAuth = auth[authKey];

  // 有权限时直接渲染内容
  if (hasAuth) {
    return children;
  } else {
    // 无权限时显示指定 UI
    return fallback || NoAuth;
  }
}

export default Auth;

使用如下:

function Foo() {
  return (
    <Auth authKey={'starRepo'}>
      <Button type="button">Star</Button>
    </Auth>
  );
}

关于接口鉴权,一般是通过后端根据用户携带的token信息或者请求头来判读的。

以上便是用ice来实现权限管理的使用方法,希望对你有所帮助。