最近在做一個網站類型的項目,要對用戶的訪問模塊(權限)進行控制,所以設計并實現了一套簡單的權限控制功能。
1. 數據庫設計
用戶:users
模塊:modules
SQL代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
/* Target Server Type : MYSQL Target Server Version : 50628 File Encoding : 65001 Date : 2016-08-26 10:35:28 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `modules` -- ---------------------------- DROP TABLE IF EXISTS `modules`; CREATE TABLE `modules` ( `id` int (10) NOT NULL AUTO_INCREMENT, `module` varchar (30) DEFAULT NULL COMMENT '模塊' , `pid` int (10) DEFAULT NULL COMMENT '上一級id' , ` level ` int (4) DEFAULT NULL COMMENT '級別' , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of modules -- ---------------------------- -- ---------------------------- -- Table structure for `users` -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `user_code` varchar (10) NOT NULL COMMENT '用戶代碼' , `user_name` varchar (40) DEFAULT NULL COMMENT '用戶名' , `user_password` varchar (100) DEFAULT NULL COMMENT '密碼' , `qq` varchar (15) DEFAULT NULL COMMENT 'qq' , `msn` varchar (50) DEFAULT NULL COMMENT 'msn' , `demo` varchar (100) DEFAULT NULL COMMENT '備注' , `auth_code` text COMMENT '權限碼' , PRIMARY KEY (`user_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of users -- ---------------------------- |
1. 后端實現
項目中用SSM+freemarker框架,把權限封裝成權限樹的數據結構,然后轉成json格式。
1) 展示層采用ztree樹(setUserauthOnTree.html)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
<!DOCTYPE html> < html > < head > <#include "common/res.html" /> < script src = "${base.ctx}/js/layer-v2.1/laypage/laypage.js" ></ script > < link href = "${base.ctx}/js/layer-v2.1/laypage/skin/laypage.css" rel = "stylesheet" type = "text/css" /> < script src = "${base.ctx}/js/layer-v2.1/layer/layer.js" ></ script > <!-- 引入樹形菜單樣式 --> < link href = "${base.ctx}/component/ztree/css/zTreeStyle/zTreeStyle.css" rel = "stylesheet" type = "text/css" /> < script type = "text/javascript" src = "${base.ctx}/component/ztree/js/jquery.ztree.core-3.5.js" ></ script > < script type = "text/javascript" src = "${base.ctx}/component/ztree/js/jquery.ztree.excheck-3.5.js" ></ script > < style type = "text/css" > .blue-madison { border: 1px solid #7ca7cc; border-top: 0; } .caption { background-color: #578ebe; border-bottom: 0; padding: 0 10px; margin-bottom: 0; color: #fff; } </ style > </ head > < body > < div class = "portlet-body" style = "overflow-y:auto; width:400px; height:550px;" > < div id = "ztree" > < ul id = "treeDemo" class = "ztree" ></ ul > </ div > </ div > < div class = "form-actions" > < div class = "row" > < div class = "col-sm-12" align = "center" style = "margin-top: 5px" > < button type = 'button' class = "btn btn-primary" onclick = "editModle()" >確定</ button > < button type = "button" class = "btn btn-primary" id = "cancel" >關閉</ button > </ div > </ div > </ div > < script > $("document").ready(function() { $.ajax({ type : "post", url : "${base.ctx}/Setup/getUserRightMaskById", data:{"id":"${userId}"}, dataType : "json", success : function(result) { zTreeObj = $.fn.zTree.init($("#treeDemo"), setting,result.datas.data); zTreeObj.expandAll(true); }, error : function() { } }); }); //加載樹 var zTreeObj; // zTree 的參數配置,深入使用請參考 API 文檔(setting 配置詳解) var setting = { view : { //dblClickExpand : false, showLine : true, //是否顯示節點間的連線 }, check: { enable: true, //nocheckInherit: false, chkStyle: "checkbox", chkboxType: { "Y": "ps", "N": "ps" }, //autoCheckTrigger: true }, callback : { onCheck: zTreeOnCheck, } }; //checkbox點擊的回調事件 function zTreeOnCheck(event, treeId, treeNode) { /* var zTree = $.fn.zTree.getZTreeObj("treeDemo"); var changedNodes = zTree.getChangeCheckedNodes(); for ( var i=0 ; i < changedNodes.length ; i++ ){ var treeNode = changedNodes [i]; } */ }; function editModle(){ var rootId = null ; var midId = null ; var minId = null ; var treeObj = $.fn.zTree.getZTreeObj("treeDemo"); var nodes = treeObj .getCheckedNodes(); for(var i = 0 ;i<nodes.length;i++){ if(nodes[i].level==0){ rootId =rootId+","+nodes[i].id; } if(nodes[i].level==1){ midId =midId+","+nodes[i].id; } if(nodes[i].level==2){ minId =minId+","+nodes[i].id; } } if(rootId!=null){ rootId =rootId.substring(5,rootId.length); } if(midId!=null){ midId =midId.substring(5,midId.length); } if(minId!=null){ minId =minId.substring(5,minId.length); } $.ajax({ type : "post", url : "${base.ctx}/Setup/updateUserRightMaskByAjax", dataType : "json", data:{"rootId":rootId,"midId":midId,"minId":minId,"userId":"${userId}"}, success : function(result) { if(result=="1"){ layer.msg("賦權成功!"); setTimeout(function(){top.dialog.get("set-dialog").close().remove();} , 600); } }, error : function() { layer.msg("系統錯誤,請聯系管理員!"); } }); } //關閉 $("#cancel").click(function() { top.dialog.get("set-dialog").close().remove(); }); </script> </ body > </ html > |
展示效果如下:
2) controller控制層用springmvc
在控制層把數據轉成json格式,發到展示層。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/** * @fun 獲取分店用戶權限 * @author 皮鋒 * @date 2016/8/25 * @param session * @param id * @param substoreid * @return */ @RequestMapping ( "getUserRightMaskById" ) @ResponseBody public Object getUserRightMaskById(HttpSession session,String id,String substoreid){ substoreid=StringUtils.isEmpty(substoreid)?String.valueOf(session.getAttribute( "substoreid" )):substoreid; //判斷是酒店還是客棧 List<Map<String, Object>> versionsList= this .setupService.getHotelHotelVersions(substoreid); Object versions=versionsList.get( 0 ).get( "versions" ); Map<String, Object> hotelMap= new HashMap<String, Object>(); if (( null !=versionsList)&&(versionsList.size()!= 0 )){ //list不為空 if ( "complete" .equals(versions)){ //酒店 //查詢酒店權限樹 hotelMap= this .rightMaskService.getUserRightMaskOnTree(substoreid,id, "complete" ); } else if ( "simple" .equals(versions)){ //客棧 //查詢客棧權限樹 hotelMap= this .rightMaskService.getUserRightMaskOnTree(substoreid,id, "simple" ); } } Map<String, Object> resultMap = new HashMap<String, Object>(); resultMap.put( "datas" , hotelMap); return JSONObject.toJSONString(resultMap, SerializerFeature.WriteMapNullValue); } |
3)service服務層把權限封裝成滿足ztree格式的樹數據結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
/** * @fun 獲取分店用戶權限 * @author 皮鋒 * @date 2016/8/25 * @param substoreid * @param id * @param versions * @return Map<String, Object> */ @Override public Map<String, Object> getUserRightMaskOnTree(String substoreid, String id, String versions) { Map<String, Object> userRightMask= this .iRightMaskDao.getUserRightMaskBySubAndId(substoreid,id); List<Map<String, Object>> listOne = new ArrayList<Map<String,Object>>(); List<Map<String, Object>> listTwo = new ArrayList<Map<String,Object>>(); //List<Map<String, Object>> listThree = new ArrayList<Map<String,Object>>(); List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>(); if (versions.equals( "complete" )){ //酒店 listOne = this .iRightMaskDao.getRightMaskOnHotelOne(); listTwo = this .iRightMaskDao.getRightMaskOnHotelTwo(); //listThree = this.iRightMaskDao.getRightMaskOnHotelThree(); packagingToTwoTree(resultList,listOne,listTwo,userRightMask); } else if (versions.equals( "simple" )){ //客棧 listOne = this .iRightMaskDao.getRightMaskOnTavernOne(); listTwo = this .iRightMaskDao.getRightMaskOnTavernTwo(); //listThree = this.iRightMaskDao.getRightMaskOnTavernThree(); packagingToTwoTree(resultList,listOne,listTwo,userRightMask); } Map<String, Object> map = new HashMap<String, Object>(); map.put( "data" , resultList); return map; } /** * @function 封裝一個一級樹 * @author 皮鋒 * @date 2016/8/26 * @param resultList * @param listOne * @param authCode * @return void */ private void packagingToOneTree(List<Map<String, Object>> resultList, List<Map<String, Object>> listOne, Map<String, Object> authCode) { for ( int i = 0 ; i < listOne.size(); i++) { Map<String, Object> rootMap = new HashMap<String, Object>(); rootMap.put( "id" , listOne.get(i).get( "id" )); rootMap.put( "name" , listOne.get(i).get( "module" )); if (validateRightMask(listOne, authCode, i) != - 1 ) { rootMap.put( "checked" , true ); } else { rootMap.put( "checked" , false ); } resultList.add(rootMap); } } /** * @function 封裝一個二級樹 * @author 皮鋒 * @date 2016/8/26 * @param resultList * @param listOne * @param listTwo * @param authCode * @return void */ private void packagingToTwoTree(List<Map<String, Object>> resultList, List<Map<String, Object>> listOne, List<Map<String, Object>> listTwo, Map<String, Object> authCode) { for ( int i = 0 ; i < listOne.size(); i++) { List<Map<String, Object>> midList = new ArrayList<Map<String, Object>>(); for ( int j = 0 ; j < listTwo.size(); j++) { if (listTwo.get(j).get( "pid" ).toString() .equals(listOne.get(i).get( "id" ).toString())) { List<Map<String, Object>> minlist = new ArrayList<Map<String, Object>>(); Map<String, Object> midMap = new HashMap<String, Object>(); midMap.put( "id" , listTwo.get(j).get( "id" )); midMap.put( "name" , listTwo.get(j).get( "module" )); midMap.put( "children" , minlist); if (validateRightMask(listTwo, authCode, j) != - 1 ) { midMap.put( "checked" , true ); } else { midMap.put( "checked" , false ); } midList.add(midMap); } } Map<String, Object> rootMap = new HashMap<String, Object>(); rootMap.put( "id" , listOne.get(i).get( "id" )); rootMap.put( "name" , listOne.get(i).get( "module" )); rootMap.put( "children" , midList); if (validateRightMask(listOne, authCode, i) != - 1 ) { rootMap.put( "checked" , true ); } else { rootMap.put( "checked" , false ); } resultList.add(rootMap); } } /** * @function 封裝一個三級樹 * @author 皮鋒 * @date 2016/8/26 * @param resultList * @param listOne * @param listTwo * @param listThree * @param authCode * @return void */ private void packagingToThreeTree(List<Map<String, Object>> resultList, List<Map<String, Object>> listOne, List<Map<String, Object>> listTwo, List<Map<String, Object>> listThree, Map<String, Object> authCode) { for ( int i = 0 ; i < listOne.size(); i++) { List<Map<String, Object>> midList = new ArrayList<Map<String, Object>>(); for ( int j = 0 ; j < listTwo.size(); j++) { if (listTwo.get(j).get( "pid" ).toString() .equals(listOne.get(i).get( "id" ).toString())) { List<Map<String, Object>> minlist = new ArrayList<Map<String, Object>>(); for ( int k = 0 ; k < listThree.size(); k++) { Map<String, Object> minMap = new HashMap<String, Object>(); if (listThree.get(k).get( "pid" ).toString() .equals(listTwo.get(j).get( "id" ).toString())) { minMap.put( "id" , listThree.get(k).get( "id" )); minMap.put( "name" , listThree.get(k).get( "module" )); if (validateRightMask(listThree, authCode, k) != - 1 ) { minMap.put( "checked" , true ); } else { minMap.put( "checked" , false ); } minlist.add(minMap); } } Map<String, Object> midMap = new HashMap<String, Object>(); midMap.put( "id" , listTwo.get(j).get( "id" )); midMap.put( "name" , listTwo.get(j).get( "module" )); midMap.put( "children" , minlist); if (validateRightMask(listTwo, authCode, j) != - 1 ) { midMap.put( "checked" , true ); } else { midMap.put( "checked" , false ); } midList.add(midMap); } } Map<String, Object> rootMap = new HashMap<String, Object>(); rootMap.put( "id" , listOne.get(i).get( "id" )); rootMap.put( "name" , listOne.get(i).get( "module" )); rootMap.put( "children" , midList); if (validateRightMask(listOne, authCode, i) != - 1 ) { rootMap.put( "checked" , true ); } else { rootMap.put( "checked" , false ); } resultList.add(rootMap); } } /** * @function 驗證authCode中是否有list中的權限碼 * @author 皮鋒 * @date 2016/8/26 * @param list * @param authCode * @param i * @return int */ private int validateRightMask(List<Map<String, Object>> list, Map<String, Object> authCode, int i) { String rightMask = authCode.get( "auth_code" ) != null ? authCode.get( "auth_code" ).toString() : "" ; if (!StringUtils.isEmpty(rightMask)) { rightMask = rightMask.replace( ";" , "," ); String[] arry = rightMask.split( "," ); for ( int j = 0 ; j < arry.length; j++) { String arryRightMask = arry[j]; String listRightMask = list.get(i).get( "id" ).toString(); if (arryRightMask.equals(listRightMask)) { return 1 ; } } } else { return - 1 ; } return - 1 ; } |
4) dao層查詢數據庫獲得用戶權限
a.在數據層按權限級別從modules表中分別拿出不同級別的權限
1
2
3
|
select id,module,pid, level from modules where level = '0' select id,module,pid, level from modules where level = '1' select id,module,pid, level from modules where level = '2' |
b.在users表中拿出某用戶的所有權限(權限碼)
select auth_code from users where user_code='pifeng'
c.保存權限時不同級別之間的權限碼用英式分號“;”隔開,同一級別之間的權限碼用英式逗號“,”隔開。例如:1,2,3,4,5,6,7,8,9,10,11,12;13,14,15,16,17,18,19,20,21,22,23,24,25,26,36,37,27,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,56,57,58,59,60,61,62,63,64,133,65,66,67,68,69,70,71,72,73,74,75,126,127,128,129,130,131,76,77,78,79,80,81,82,83,84,85,86,87,88,99,124,134,135,136,140,141,89,90,91,92,93,94,95,96,97,98,137,138,139,100,101,102,103,106,107,132,108,109,110,111,112,113,114,115,116,125,117,118,119,120,121,122
5)根據用戶的權限碼用freemarker標簽控制頁面功能模塊是否顯示
a.freemarker在xml文件中的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<bean id= "freemarkerConfig" class = "org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer" > <!--模板加載路徑--> <property name= "templateLoaderPath" > <value>/WEB-INF/ftl/</value> </property> <property name= "freemarkerVariables" > <map> <entry key= "xml_escape" value-ref= "fmXmlEscape" /> </map> </property> <property name= "freemarkerSettings" > <props> <prop key= "tag_syntax" >auto_detect</prop> <prop key= "template_update_delay" > 0 </prop> <prop key= "default_encoding" >UTF- 8 </prop> <prop key= "output_encoding" >UTF- 8 </prop> <prop key= "locale" >zh_CN</prop> <prop key= "date_format" >yyyy-MM-dd</prop> <prop key= "time_format" >HH:mm:ss</prop> <prop key= "number_format" > 0 .######</prop> <prop key= "datetime_format" >yyyy-MM-dd HH:mm:ss</prop> <!--空值處理--> <prop key= "classic_compatible" > true </prop> <!--自動導入ftl模板,并以“base”別名作為命名空間--> <prop key= "auto_import" >inc/spring.ftl as base</prop> </props> </property> </bean> <bean id= "fmXmlEscape" class = "freemarker.template.utility.XmlEscape" /> <bean id= "freeMarkerViewResolver" class = "org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver" > <property name= "suffix" value= ".html" /> <property name= "cache" value= "false" /> <property name= "viewClass" value= "org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> <property name= "contentType" value= "text/html;charset=UTF-8" ></property> <!--fixed Exception:Cannot expose session attribute 'substoreid' because of an existing model --> <property name= "allowSessionOverride" value= "true" /> <property name= "exposeRequestAttributes" value= "true" /> <property name= "exposeSessionAttributes" value= "true" /> <property name= "exposeSpringMacroHelpers" value= "true" /> <!-- 此變量值為pageContext.request, 頁面使用方法:request.contextPath --> <property name= "requestContextAttribute" value= "request" /> <property name= "attributesMap" > <map> <!-- 定義Freemarker方法的名稱 --> <entry key= "menucall" > <!-- 關聯到我們之前定義的工具類 --> <bean class = "com.leike.util.MenuFunction" /> </entry> </map> </property> </bean> |
b.寫個類繼承TemplateMethodModel類,實現freemarker自定義方法,用于實現控制頁面模塊是否顯示
登陸的時候把用戶權限碼存入session中,然后從session中取權限。下面是一個例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public class MenuFunction implements TemplateMethodModel{ @Override public Object exec(List arg0) throws TemplateModelException { int level = Integer.valueOf(arg0.get( 0 ).toString()); //模塊等級 int modelId=Integer.valueOf(arg0.get( 1 ).toString()); //模塊id int count= 0 ; //記錄session是否有此模塊的權限碼 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session=request.getSession(); Object o = session.getAttribute( "info" ); if (o== null ) return false ; Info info = (Info) o; String authCode=info.getUser().getAuthCode(); //權限碼 if (authCode.contains( ";" )){ String[] masks=authCode.split( ";" ); String[] m=masks[level].split( "," ); for ( int i = 0 ; i < m.length; i++) { if (modelId==Integer.parseInt(m[i])){ ++count; } else { count+= 0 ; } } } if (count== 0 ){ return false ; } else { return true ; } } } |
c.在頁面使用freemarker標簽,控制模塊的顯示隱藏
Menucall中的兩個參數,第一個為模塊等級,第二個為模塊的id
例如:
1
2
3
4
5
|
<# if menucall( 1 , 122 )> <li style= "line-height: 250%" > <a href= "#" id= "booknew" ><i class = "glyphicon" ></i>預訂</a> </li> </# if > |
以上就是對用戶的訪問模塊(權限)進行控制的大體實現。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。