8 BOS項目第08天
對權(quán)限涉及的5張表拟枚,進(jìn)行CRUD的操作
8.1 初始化權(quán)限數(shù)據(jù)
-
權(quán)限功能需要一張權(quán)限表記錄數(shù)據(jù)
-
重要字段
- page:訪問路徑
- generatemenu:是否生成菜單
- zindex:優(yōu)先級,實現(xiàn)排序
- pid:父級目錄的id
打開
auth_function.sql
,復(fù)制到Navicat
8.2 功能權(quán)限管理列表
8.2.1 數(shù)據(jù)列表展示
- 寫Dao,Service,Action,Jsp代碼
- model中的是generatemenu,需要修改jsp中對應(yīng)的字段
generateMenu - url改為:
${pageContext.request.contextPath}/functionAction_pageQuery.action
- 加上分頁代碼:
- model中的是generatemenu,需要修改jsp中對應(yīng)的字段
pageList:[5,10,15],
pageSize:10,
pagination:true,
- exclude三個屬性
- 排除Function模型中兩個會造成循環(huán)引用的屬性:
"function", "functions"
- 權(quán)限與角色是多對多關(guān)系,
權(quán)限表
不用直接顯示角色屬性,所以要exclude屬性roles
- 排除Function模型中兩個會造成循環(huán)引用的屬性:
- 解決分頁的Bug
-
因為Function和BaseAction都有參數(shù)page,page參數(shù)會優(yōu)先傳遞給Model,分頁無法獲取參數(shù),一直默認(rèn)為第一頁.解決方案就是手動給PageBean賦值
-
8.2.2 功能權(quán)限信息添加
- 把jsp中的id換成關(guān)鍵字,id不需要自己輸入
- 是否生成關(guān)菜單,用select表示,1用生成表示,0用不生成表示,注意,這里的
generateMenu也要改成小寫
{
field : 'generatemenu',
title : '是否生成菜單',
formatter : function(data, row, index){
if(data=="1"){
return "生成";
}else{
return "不生成";
}
},
width : 200
},
父功能點:parentFunction.id改成function.id,要與模型對應(yīng)
-
實現(xiàn)save業(yè)務(wù)鏈
- Jsp:在表單上提供action屬性:
${pageContext.request.contextPath}/functionAction_save.action
- save按鈕的點擊事件:
$("#functionForm").submit();
- Jsp:在表單上提供action屬性:
-
顯示父功能點,后臺返回Json,textField展示name
- JSP部分
<input name="function.id" class="easyui-combobox" data-options="valueField:'id',textField:'name',url:'${pageContext.request.contextPath}/functionAction_listJson.action'"/>
- Action部分
- JSP部分
public void listJson(){
List<Function> functions = functionService.findAll();
responseJson(functions, new String[]{"function", "functions"});
}
- 是否生成
8.3 角色管理
8.3.1 添加角色功能
8.3.2 修改授權(quán)數(shù)據(jù)的來源
- 分析授權(quán)數(shù)據(jù)的獲取源碼
- 本質(zhì)就是將
基礎(chǔ)功能
的樹,再次顯示 - 從
權(quán)限表
獲取數(shù)據(jù):${pageContext.request.contextPath}/functionAction_listJson.action
- 后臺返回的json,要提供pId屬性
- Fuction代碼
- 本質(zhì)就是將
private String pId;
public String getpId() {
if(function != null){
return function.getId();
}
return "0";
}
- 獲取授權(quán)數(shù)據(jù),勾選的所有id
var treeObj = $.fn.zTree.getZTreeObj("functionTree");
var nodes = treeObj.getCheckedNodes(true);
var ids = new Array();
for(var i = 0; i < nodes.length; i++){
var id = nodes[i].id;
ids.push(id);
}
//拼接所有id
var idsStr = ids.join(",");
8.3.4 數(shù)據(jù)提交與后臺處理
- 表單提交
- JSP將顯示id換成關(guān)鍵字
- 添加一個隱藏的input,名字為ids,用于提交參數(shù)
- URL:
"${pageContext.request.contextPath}/roleAction_save.action"
- 后臺處理
- DAO,Service
- 添加一個RoleAction來處理表單提交
- 中間表由Hibernate自動維護(hù),只需寫好映射即可
@Override
public void save(Role role, String functionIds) {
//1.保存角色
roleDao.save(role);
//2.添加角色,權(quán)限,中間表ids
//2.1拆分id
String[] functionIdsArr = functionIds.split(",");
for (String functionId : functionIdsArr) {
//把id封裝成Function模型
Function function = new Function();
function.setId(functionId);
//把function存在Role里面去
role.getFunctions().add(function);//內(nèi)部執(zhí)行insert語句
}
}
8.3.5 角色管理列表實現(xiàn)
- pageQuery方法,簡單實現(xiàn)
url : '${pageContext.request.contextPath}/roleAction_pageQuery.action?page=1&rows=20',
8.4 用戶管理模塊
8.4.1 顯示用戶列表數(shù)據(jù)
- UserAction中實現(xiàn)分頁查詢
- 幾個注意點:
- 之前的測試賬戶admin,要刪掉,因為工資,生日等數(shù)據(jù)都為空,會造成轉(zhuǎn)JSON異常
- Data是util包下的,而不是sql包下的
- User模型提供一個getBirthdayStr方法,對應(yīng)birthdayStr屬性,便于轉(zhuǎn)換日期格式
- remark字段不顯示,所以也應(yīng)該exclude,roles也要exclude
8.4.2 添加用戶
-
效果圖
首先需要獲得角色數(shù)據(jù),Action代碼
public void listJson() throws IOException {
List<Role> roles = roleService.findAll();
responseJson(roles, new String[]{"users", "functions"});
}
- JS動態(tài)添加checkbox
//獲取角色數(shù)據(jù)
var url = "${pageContext.request.contextPath}/roleAction_listJson.action";
$.post(url, function (data) {
for (var i = 0;i<data.length;i++) {
var name = data[i].name;
var id = data[i].id;
var inputTag = '<input type="checkbox" value="'+id+'" name="roleIds">' + name;
$("#roleTd").append(inputTag);
}
});
- 查看表單請求參數(shù)
- userAction的添加方法實現(xiàn)
- userService的實現(xiàn)
@Override
public void save(User model, String[] roleIds) {
//密碼使用MD5
String pwd = MD5Utils.text2md5(model.getPassword());
model.setPassword(pwd);
userDao.save(model);
for (String roleId : roleIds) {
Role role = new Role();
role.setId(roleId);
model.getRoles().add(role);
}
}
- 用戶列表中生日的顯示,util包中Date格式的日期返回的是
默認(rèn)返回的生日數(shù)據(jù)格式如圖
{
"birthday": {
"date": 1,
"day": 4,
"hours": 0,
"minutes": 0,
"month": 5,
"nanos": 0,
"seconds": 0,
"time": 1496246400000,
"timezoneOffset": -480,
"year": 117
}
- 給User模型添加一個get方法,修改界面字段為birthdayStr,這再次說明了屬性是get方法截取后的產(chǎn)物
public String getBirthdayStr(){
if (birthday != null){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(birthday);
}else{
return "未提交生日";
}
}
- 把界面的電話字段改成顯示角色
1 在User模型中添加一個getRolesStr方法
public String getRolesStr(){
String str = "";
for (Role role : roles) {
str += role.getName() + "栽连、";
}
return str;
}
-
效果
8.5 修改BOSRealm中的授權(quán)方法
8.5.1 授權(quán)的邏輯
- 如果是admin,則擁有所有權(quán)限(Function所有的功能都能用)
- 如果是非管理員,只能通過
sql
語句,用用戶id查找權(quán)限- 一個用戶,可能有多個角色,一個角色又有多個權(quán)限,那么通過id查詢權(quán)限,肯定有重復(fù)的權(quán)限數(shù)據(jù),所以要DISTINCT,去除重復(fù)數(shù)據(jù)
- 推薦第二種寫法,易于理解,三張主表都是多對多關(guān)系,使用左外連接
#根據(jù)用戶ID,查找他所擁有的的權(quán)限function
SELECT f.id, f.name, f.page, f.code
FROM auth_function f
LEFT OUTER JOIN role_function rf
ON rf.function_id = f.id
LEFT OUTER JOIN auth_role r
ON rf.role_id = r.id
LEFT OUTER JOIN user_role ur
ON r.id = ur.role_id
WHERE ur.user_id = '40289f196c9f9da7016c9fa12f3a0000';
#第二種方式
SELECT DISTINCT f.id,f.name,f.page,f.code
FROM
auth_function f,
role_function rf,
auth_role r,
user_role ur
WHERE
rf.function_id = f.id and
rf.role_id = r.id and
r.id = ur.role_id and
ur.user_id = '40289f196c9f9da7016c9fa12f3a0000';
- 用戶查找權(quán)限hql語句
@Override
public List<Function> findListByUserId(String userId) {
String hql = "SELECT DISTINCT f FROM Function f ";
hql += "LEFT OUTER JOIN f.roles r ";
hql += "LEFT OUTER JOIN r.users u ";
hql += "WHERE u.id = ?";
return hibernateTemplate.find(hql, userId);
}
- 給登錄用戶,授予權(quán)限,Realm代碼
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//拿到用戶
User loginUser = (User) principals.getPrimaryPrincipal();
//根據(jù)用戶ID查權(quán)限
List<Function> functions = null;
//admin超級管理員
if (loginUser.getUsername().equals("admin")){
functions = functionDao.findAll();
}else{
functions = functionDao.findListByUserId(loginUser.getId());
}
//往shiro添加權(quán)限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (Function function : functions) {
info.addStringPermission(function.getCode());
}
return info;
}
-
授權(quán)測試:JSP添加一個shiro標(biāo)簽
-
在數(shù)據(jù)庫中添加一個admin
這樣用hr登錄,就沒有作廢功能,作廢按鈕不顯示
-
對數(shù)據(jù)庫的一點補充
- 左外連接,就是左連接,此二者等價
- 還有全外連接,一般不用
-
多對多關(guān)系,本質(zhì)就是通過中間表,做所有的連接,然后查詢想要的數(shù)據(jù),并去除重復(fù)
8.6 使用ehcache緩存權(quán)限數(shù)據(jù)
-
導(dǎo)入ehcache的jar包,3個
使用ehcache緩存權(quán)限數(shù)據(jù)
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
- 在spring配置文件中添加授權(quán)緩存策略
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
</bean>
<!--2.配置shiro的安全管理者-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--配置realm對象-->
<property name="realm" ref="realm"></property>
<!--配置緩存管理者-->
<property name="cacheManager" ref="cacheManager"></property>
</bean>
<!--3.配置一個realm對象,這個對象要繼承AuthorizingRealm-->
<bean id="realm" class="com.kdj.bos.web.realm.BOSRealm"></bean>
8.7 修改主界面菜單從數(shù)據(jù)庫獲取
- 主界面菜單根據(jù)登錄用戶的角色不用來顯示不同的菜單,管理員擁有全部功能
- dao代碼
@Override
public List<Function> findMenuByUserId(String id) {
String hql = "SELECT DISTINCT f From Function f ";
hql += "LEFT OUTER JOIN f.roles r ";
hql += "LEFT OUTER JOIN r.users u ";
hql += "WHERE u.id = ? AND f.generatemenu = '1' ORDER BY f.zindex DESC";
return (List<Function>) this.hibernateTemplate.find(hql,id);
}
@Override
public List<Function> findAllMenu() {
String hql = "From Function f where f.generatemenu = '1' ORDER BY f.zindex DESC";
return (List<Function>) this.hibernateTemplate.find(hql);
}
- 前后端交互的本質(zhì):前端發(fā)送一個請求,后端返回一個json,就這么簡單