<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>RBAC on Saiga</title>
    <link>http://localhost:1313/tags/rbac/</link>
    <description>Recent content in RBAC on Saiga</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <managingEditor>wuwenzen@outlook.com (wuwj)</managingEditor>
    <webMaster>wuwenzen@outlook.com (wuwj)</webMaster>
    <lastBuildDate>Fri, 02 Apr 2021 00:00:00 +0000</lastBuildDate>
    <atom:link href="http://localhost:1313/tags/rbac/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>菜单、按钮、接口：一套权限系统怎么落地到前端</title>
      <link>http://localhost:1313/posts/2021-04-02-permission-system-design/</link>
      <pubDate>Fri, 02 Apr 2021 00:00:00 +0000</pubDate><author>wuwenzen@outlook.com (wuwj)</author>
      <guid>http://localhost:1313/posts/2021-04-02-permission-system-design/</guid>
      <description>&lt;p&gt;中后台项目到了一定体量之后，绕不开一个问题：&lt;strong&gt;权限怎么做？&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;不同角色看到的菜单不一样；&lt;/li&gt;&#xA;&lt;li&gt;同一个页面里，有人能点「导出」、有人只能看；&lt;/li&gt;&#xA;&lt;li&gt;接口层还要做真正的权限校验。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这篇文章从实践角度，串一下我现在常用的一套设计：从「角色-权限-资源」模型，到前端路由、按钮显隐，再到接口鉴权。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;1-模型角色权限点资源&#34;&gt;1. 模型：角色、权限点、资源&lt;/h2&gt;&#xA;&lt;p&gt;先把概念讲清楚：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用户（User）&lt;/strong&gt;：具体的人；&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;角色（Role）&lt;/strong&gt;：一类权限集合，例如「运营」、「风控」、「管理员」；&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;权限点（Permission）&lt;/strong&gt;：最小粒度的能力，比如「订单列表查看」「订单导出」；&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;资源（Resource）&lt;/strong&gt;：被操作的对象，比如菜单、按钮、接口。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;关系一般是：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;User → 多个 Role → 多个 Permission → 作用在不同 Resource 上&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;前端最关心的其实只有一件事：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;登录之后，我拿到的「权限点列表」是什么？&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;因为&lt;strong&gt;菜单、按钮显隐、前端路由保护&lt;/strong&gt;，本质上都是围绕这串权限点做判断。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;2-权限点命名模块--资源--动作&#34;&gt;2. 权限点命名：模块 + 资源 + 动作&lt;/h2&gt;&#xA;&lt;p&gt;命名如果乱，后面就会非常难维护。实践中我会用统一格式：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;模块&amp;gt;:&amp;lt;资源&amp;gt;:&amp;lt;动作&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;order:list:view&lt;/code&gt; —— 订单列表查看；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;order:list:export&lt;/code&gt; —— 订单列表导出；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;order:detail:update&lt;/code&gt; —— 订单详情修改；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;user:manage:create&lt;/code&gt; —— 用户管理新增。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在数据库或配置里，用一个简单的结构描述：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Permission&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;      &lt;span style=&#34;color:#75715e&#34;&gt;// order:list:view&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;      &lt;span style=&#34;color:#75715e&#34;&gt;// 订单列表-查看&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;description?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;前端拿到的就是一串 &lt;code&gt;code&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;order:list:view&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;order:list:export&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;order:detail:update&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h2 id=&#34;3-菜单与路由根据权限生成&#34;&gt;3. 菜单与路由：根据权限生成&lt;/h2&gt;&#xA;&lt;h3 id=&#34;31-菜单数据从后端来&#34;&gt;3.1 菜单数据从后端来&lt;/h3&gt;&#xA;&lt;p&gt;让后端维护一份菜单树：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MenuItem&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parentId?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;icon?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 访问该菜单需要的权限点（可以是单个或多个）&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;permission?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;children?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;MenuItem&lt;/span&gt;[];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;登录成功后，前端请求：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
