123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- <!DOCTYPE html>
- <html>
- <head>
- <title>privilege_权限控制设计思路</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <style type="text/css">
- /* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
- /* Author: Nicolas Hery - http://nicolashery.com */
- /* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
- /* Source: https://github.com/nicolahery/markdownpad-github */
- /* RESET
- =============================================================================*/
- html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- }
- /* BODY
- =============================================================================*/
- body {
- font-family: Helvetica, arial, freesans, clean, sans-serif;
- font-size: 14px;
- line-height: 1.6;
- color: #333;
- background-color: #fff;
- padding: 20px;
- max-width: 960px;
- margin: 0 auto;
- }
- body>*:first-child {
- margin-top: 0 !important;
- }
- body>*:last-child {
- margin-bottom: 0 !important;
- }
- /* BLOCKS
- =============================================================================*/
- p, blockquote, ul, ol, dl, table, pre {
- margin: 15px 0;
- }
- /* HEADERS
- =============================================================================*/
- h1, h2, h3, h4, h5, h6 {
- margin: 20px 0 10px;
- padding: 0;
- font-weight: bold;
- -webkit-font-smoothing: antialiased;
- }
- h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
- font-size: inherit;
- }
- h1 {
- font-size: 28px;
- color: #000;
- }
- h2 {
- font-size: 24px;
- border-bottom: 1px solid #ccc;
- color: #000;
- }
- h3 {
- font-size: 18px;
- }
- h4 {
- font-size: 16px;
- }
- h5 {
- font-size: 14px;
- }
- h6 {
- color: #777;
- font-size: 14px;
- }
- body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
- margin-top: 0;
- padding-top: 0;
- }
- a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
- margin-top: 0;
- padding-top: 0;
- }
- h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
- margin-top: 10px;
- }
- /* LINKS
- =============================================================================*/
- a {
- color: #4183C4;
- text-decoration: none;
- }
- a:hover {
- text-decoration: underline;
- }
- /* LISTS
- =============================================================================*/
- ul, ol {
- padding-left: 30px;
- }
- ul li > :first-child,
- ol li > :first-child,
- ul li ul:first-of-type,
- ol li ol:first-of-type,
- ul li ol:first-of-type,
- ol li ul:first-of-type {
- margin-top: 0px;
- }
- ul ul, ul ol, ol ol, ol ul {
- margin-bottom: 0;
- }
- dl {
- padding: 0;
- }
- dl dt {
- font-size: 14px;
- font-weight: bold;
- font-style: italic;
- padding: 0;
- margin: 15px 0 5px;
- }
- dl dt:first-child {
- padding: 0;
- }
- dl dt>:first-child {
- margin-top: 0px;
- }
- dl dt>:last-child {
- margin-bottom: 0px;
- }
- dl dd {
- margin: 0 0 15px;
- padding: 0 15px;
- }
- dl dd>:first-child {
- margin-top: 0px;
- }
- dl dd>:last-child {
- margin-bottom: 0px;
- }
- /* CODE
- =============================================================================*/
- pre, code, tt {
- font-size: 12px;
- font-family: Consolas, "Liberation Mono", Courier, monospace;
- }
- code, tt {
- margin: 0 0px;
- padding: 0px 0px;
- white-space: nowrap;
- border: 1px solid #eaeaea;
- background-color: #f8f8f8;
- border-radius: 3px;
- }
- pre>code {
- margin: 0;
- padding: 0;
- white-space: pre;
- border: none;
- background: transparent;
- }
- pre {
- background-color: #f8f8f8;
- border: 1px solid #ccc;
- font-size: 13px;
- line-height: 19px;
- overflow: auto;
- padding: 6px 10px;
- border-radius: 3px;
- }
- pre code, pre tt {
- background-color: transparent;
- border: none;
- }
- kbd {
- -moz-border-bottom-colors: none;
- -moz-border-left-colors: none;
- -moz-border-right-colors: none;
- -moz-border-top-colors: none;
- background-color: #DDDDDD;
- background-image: linear-gradient(#F1F1F1, #DDDDDD);
- background-repeat: repeat-x;
- border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
- border-image: none;
- border-radius: 2px 2px 2px 2px;
- border-style: solid;
- border-width: 1px;
- font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
- line-height: 10px;
- padding: 1px 4px;
- }
- /* QUOTES
- =============================================================================*/
- blockquote {
- border-left: 4px solid #DDD;
- padding: 0 15px;
- color: #777;
- }
- blockquote>:first-child {
- margin-top: 0px;
- }
- blockquote>:last-child {
- margin-bottom: 0px;
- }
- /* HORIZONTAL RULES
- =============================================================================*/
- hr {
- clear: both;
- margin: 15px 0;
- height: 0px;
- overflow: hidden;
- border: none;
- background: transparent;
- border-bottom: 4px solid #ddd;
- padding: 0;
- }
- /* TABLES
- =============================================================================*/
- table th {
- font-weight: bold;
- }
- table th, table td {
- border: 1px solid #ccc;
- padding: 6px 13px;
- }
- table tr {
- border-top: 1px solid #ccc;
- background-color: #fff;
- }
- table tr:nth-child(2n) {
- background-color: #f8f8f8;
- }
- /* IMAGES
- =============================================================================*/
- img {
- max-width: 100%
- }
- </style>
- </head>
- <body>
- <h1>privilege_权限控制设计思路</h1>
- <blockquote>
- <p>本文档主要是简单描述下权限控制的主要部分。</p>
- </blockquote>
- <h2>基本脉络</h2>
- <blockquote>
- <p>主要分成5个部分。</p>
- </blockquote>
- <ol>
- <li>三个权限基础数据对象和URI</li>
- <li>组织基础数据对象</li>
- <li>角色、用户的权限表</li>
- <li>角色、用户的权限数据(用于访问控制、给其它用户配置权限)</li>
- <li>基于用户的权限数据生成有层级的访问菜单</li>
- </ol>
- <h2>分解</h2>
- <h3>1. 三个权限基础数据对象和URI</h3>
- <p>包含 <strong>URI.java,PNode.java, PModule.java, PMenu.java</strong> 对象</p>
- <blockquote>
- <p>URI: 字面意思,所有Controller的@RequestMapping的value都在此对象定义。<br>
- PNode: 定义最细粒度的权限控制节点,也能起到聚合控制的作用。<br>
- PModule: 定义模块和模块下拥有的多个PNode节点。<br>
- PMenu: 定义访问菜单。聚合PModule定义的模块到菜单下。初步形成有层级的访问菜单。</p>
- </blockquote>
- <p>彼此关联:</p>
- <p><img src="menu_module_node_uri_ctrl.jpg" alt="menu_module_node_uri_ctrl.jpg" /></p>
- <h3>2. 组织基础数据对象</h3>
- <p>依据PMenu的配置代码,生成树形菜单的数据结构。</p>
- <h4>Menu.java</h4>
- <pre><code>public class Menu {
- private int id; // 当前ID PMenu.ordinal() + 1;
- private int pid = 0; // 父id PMenu.ordinal() + 1
- private String name; // 菜单名称
- private List<Module> moduleList = new ArrayList<>(); // 模块列表
- private List<Menu> subList = new ArrayList<>(); // 子菜单列表
- }
- </code></pre>
- <h4>Module.java</h4>
- <pre><code>public class Module {
- private final int id; // PModule.ordinal() + 100;
- private final int pid; // Menu实例的id
- private final String pname; // 模块的值、名称
- private final String desc;
- private final List<PNode> nodeList = new ArrayList<>(); // 节点列表
- }
- </code></pre>
- <p>结合以上两数据对象,生成层级菜单(<code>List<Menu></code>),供权限配置。<br>
- 前端的层级展示采用 <a href="http://maxazan.github.io/jquery-treegrid/">jquery-treegrid</a> .</p>
- <h4><code>List<Menu></code> 数据结构示例</h4>
- <pre><code>[{
- "id": 1,
- "pid": 0,
- "name": "系统基本配置",
- "moduleList": [{
- "id": 101,
- "pid": 1,
- "pname": "privilege_role",
- "desc": "角色管理配置",
- "nodeList": ["MANAGER",
- "NEW",
- "GET",
- "EDIT"]
- },
- {
- "id": 102,
- "pid": 1,
- "pname": "privilege_user",
- "desc": "用户管理配置",
- "nodeList": ["MANAGER",
- "NEW",
- "GET",
- "EDIT"]
- }],
- "subList": []
- }]
- </code></pre>
- <h3>3. 角色、用户的权限和表</h3>
- <blockquote>
- <ol>
- <li>模块的权限表示,这里采用 <strong>索引字符</strong> 表示法。"10000000"的权限字符串,表示只有模块的PNode.MANAGER节点的访问、操作权限。(<strong>PNode.index</strong> 即是索引)</li>
- <li>权限表只有模块和模块的权限数据,没有菜单结构的数据。<br>
- <em>所以需要通过权限的模块,倒序生成出用户的访问菜单,后面会涉及。</em></li>
- </ol>
- </blockquote>
- <p>先看表数据:</p>
- <h5>角色权限表数据</h5>
- <p><img src="privilege_role.jpg" alt="privilege_role.jpg" /> <br></p>
- <p>表结构比较简单,只有模块名、角色拥有的权限。</p>
- <h5>用户权限表数据</h5>
- <p><img src="privilege_user.jpg" alt="privilege_user.jpg" /><br></p>
- <p>表结构同样只有模块名、角色拥有的权限。不过此表数据分两种情况:<br></p>
- <blockquote>
- <ol>
- <li>role_id > 0:表示基于角色给用户配置了权限。用户的权限数据直接定位到角色的权限数据。<br></li>
- <li>role_id==0: 表示此表里 <strong><code>user_id</code></strong> 指定的,即是用户的权限数据。</li>
- </ol>
- </blockquote>
- <h3>4. 角色、用户的权限数据(用于访问控制、给其它用户配置权限)</h3>
- <ol>
- <li>用户可访问的模块和模块的节点,由权限数据表示。</li>
- <li>用户给其它用户配置的权限数据在自己的权限数据范围内。</li>
- </ol>
- <h5>用于访问控制的权限数据</h5>
- <blockquote>
- <ol>
- <li>
- 生成: <br>
- 读取用户权限表数据-->对照PModule的定义将权限的字符表示转化为节点值列表。
- </li>
- <li>控制: 见 <code>MySecurityPrivilegeFilter</code> 的实现。有在 <code>web.xml</code> 配置此Filter。</li>
- <li>结构:<br></li>
- </ol>
- </blockquote>
- <pre><code>List<Map<String, List<String>>> // List<Map<PModule.pname, List<PNode.pname>>>
- 数据示例(json):
- [{
- "privilege_role": ["manager",
- "new",
- "get",
- "edit"]
- },
- {
- "privilege_user": ["manager",
- "new"]
- }]
- </code></pre>
- <h5>给其它用户配置权限的权限数据</h5>
- <h6>1. 生成:</h6>
- <p>基于用户的【访问控制的权限数据】(<code>List<Map<String, List<String>>></code>)进一步生成有结构的菜单(<strong><code>List<Menu></code></strong>)。</p>
- <h6>2. 实现:</h6>
- <p>见 <code>PrivilegeService</code>。用户登录成功时加载,仅加载一次,并加到Session属性。加载见: <code>MyAuthenticationSuccessHandler</code> 对象。</p>
- <h6>3. 数据结构范例:</h6>
- <pre><code>[{
- "id": 1,
- "pid": 0,
- "name": "系统基本配置",
- "moduleList": [{
- "id": 101,
- "pid": 1,
- "pname": "privilege_role",
- "desc": "角色管理配置",
- "nodeList": ["MANAGER",
- "NEW",
- "GET",
- "EDIT"]
- },
- {
- "id": 102,
- "pid": 1,
- "pname": "privilege_user",
- "desc": "用户管理配置",
- "nodeList": ["MANAGER",
- "NEW",
- "GET",
- "EDIT"]
- }],
- "subList": []
- }]
- </code></pre>
- <h3>5. 基于用户的权限数据生成层级访问菜单</h3>
- <h6>1. 生成:</h6>
- <p>基于用户的【访问控制的权限数据】(<code>List<Map<String, List<String>>></code>),
- 结合PMenu的菜单与子菜单定义,由模块值逆序找到模块、找到拥有此模块的菜单、继续找父菜单。生成用于访问的树形菜单(<strong><code>List<AccessMenu></code></strong>)。</p>
- <h6>2. 层级菜单的数据对象:</h6>
- <p>AccessMenu:</p>
- <pre><code>public class AccessMenu {
- private final String desc;
- private final List<AccessModule> moduleList = new ArrayList<>();
- private final List<AccessMenu> subMenuList = new ArrayList<>();
- }
- </code></pre>
- <p>AccessModule:</p>
- <pre><code>public class AccessModule {
- private final String desc;
- private final String uri;
- }
- </code></pre>
- <h6>3. 代码实现:</h6>
- <pre><code>见 PrivilegeService.mineAccessMenu()
- </code></pre>
- <h6>4. 数据结构范例:</h6>
- <pre><code>[{
- "desc": "系统基本配置",
- "moduleList": [{
- "desc": "权限基础数据",
- "uri": "/hs/privilege/manager"
- },
- {
- "desc": "角色管理配置",
- "uri": "/hs/privilege_role/manager"
- },
- {
- "desc": "用户管理配置",
- "uri": "/hs/privilege_user/manager"
- }],
- "subMenuList": [{
- "desc": "测试2",
- "moduleList": [{
- "desc": "用户管理",
- "uri": "/hs/user/manager"
- }],
- "subMenuList": []
- }]
- },
- {
- "desc": "测试",
- "moduleList": [{
- "desc": "用户管理配置",
- "uri": "/hs/privilege_user/manager"
- }],
- "subMenuList": []
- }]
- </code></pre>
- <p>其中 subMenuList 有值,现在暂时只支持一层子菜单。有需要再考虑完善。
- 上面数据的效果图:</p>
- <p><img src="access.jpg" alt="access.jpg" /><br></p>
- </body>
- </html>
- <!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
|