權限分配
下面主要來說說Thymeleaf模板使用和權限分配的實現思路。
先看效果圖:
實現該功能的思路是:傳往前端的數據爲Map,該Map以columnName(權限分欄名)爲key,以該分欄下的所有權限爲value。因爲要回顯權限是否選中,這裏爲權限實體類(Perm)定義了一個屬性checkedFlag(默認爲false)
private boolean checkedFlag = false;
角色管理功能中,首先將得到columnList,遍歷columnList,然後以columnId查詢屬於該分欄下的所有權限得到permList,根據roleId查詢權限映射表(permmapping)得到mappingList,如果某個permId既在permList中又在mappingList中,說明該權限爲選中狀態。
@GetMapping("/assignPerm")
public ModelAndView assignPerm(@RequestParam int roleId,@RequestParam String roleName){
//權限分欄集合
List<Column> columnList = columnService.findColumnList();
Role role = roleService.findRoleById(roleId);
ResultEntity resultEntity = new ResultEntity();
//獲取當前的方法名
String methodName = new Exception().getStackTrace()[0].getMethodName();
resultEntity.setResultCode("200");
resultEntity.setOperMessage(CLASS_NAME+"---"+methodName);
resultEntity.setResultObject(role);
resultEntity.setResultList(columnList);
HashMap<String, List<?>> map = new HashMap<>();
List<PermMapping> mappingList = permMappingService.findMappingListByRole(roleId);
for(Column column:columnList){
//獲取該分欄下的所有權限(permList)
List<Perm> permList= permService.findPermByColumn(column.getColumnId());
//遍歷permList
for(Perm perm:permList){
for(PermMapping permMapping:mappingList){
if(permMapping.getPermId()==perm.getPermId()){
//當映射關係集合mappingList中對象的permId與permList中對象permId相同時,將permChecked的狀態設置爲true
perm.setCheckedFlag(true);
}
}
}
//以權限所屬的columnName(分欄名)爲key,以該columnName下的permList(權限勾選對象集合)爲value
map.put(column.getColumnName(),permList);
}
resultEntity.setResultListMap(map);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("resultEntity",resultEntity);
modelAndView.setViewName("/roleOper");
return modelAndView;
}
看這部分對應的前端:
<div class="step clearfix" th:each="column:${resultEntity.resultList}">
<div class="classify">
<div class="checkbox_item">
<input type="checkbox" name="checkGroup" th:name="'checkGroup_'+${column.columnId}" value="checkbox" th:value="${column.columnName}" class="ui-checkbox" id="user" th:id="${column.columnName}">
<label for="user" th:for="${column.columnName}" class="ui-label blod" th:text="${column.columnName}">文章管理</label>
</div>
</div>
<div class="qx_item">
<div class="checkbox_item" th:each="perm:${resultEntity.resultListMap['__${column.columnName}__']}">
<input type="checkbox" value="ckeck_item" id="ckeck_item" th:checked="${perm.checkedFlag}" name="ckeck_item" th:name="'checkItem_'+${perm.columnId}" th:value="${perm.permId}" th:id="${perm.permId}" class="ui-checkbox" date="cms_manage" th:title="${perm.checkedFlag}">
<label for="ckeck_item" th:for="${perm.permId}" th:text="${perm.permName}" class="ui-label">文章分類管理</label>
</div>
</div>
</div>
從上面的代碼中可以看出,權限分欄下的所有checkbox的值爲該權限的id。Thymeleaf能夠接收Map類型的值,Map的取值方式爲
map['key']
,如果key是變量的話,取值方式爲map['__${variable}__']。
其中,權限分欄的checkbox的name屬性是以checkGroup爲前綴,以columnId爲後綴,以下劃線分隔。權限的checkbox的name屬性是以checkItem爲前綴,以它所屬的columnId爲後綴,以下劃線分隔。全選的checkbox的name屬性的值爲checkAll
th:name="'checkGroup_'+${column.columnId}"
th:name="'checkItem_'+${perm.columnId}"
name="checkAll"
下面就是js中checkbox全選和反選的邏輯:
var totalLength = $(":checkbox[name!='checkAll']").length;
var checkedLength = 0;
$(":checkbox[name^='checkGroup']").each(
function(){
var perffix = 'checkItem';
var suffix = $(this).attr('name').split('_')[1];
var itemLength = $(":checkbox[name^='"+perffix+"'][name$='"+suffix+"']").length;
var itemCheckedLength = $(":checkbox[name^='"+perffix+"'][name$='"+suffix+"']:checked").length;
if(itemCheckedLength == itemLength){
this.checked = true;
checkedLength+=itemLength+1;
}
var flag = this.checked;
$(":checkbox[name^='"+perffix+"'][name$='"+suffix+"']").each(
function(){
this.checked = flag;
}
);
if(checkedLength==totalLength){
$(":checkbox[name='checkAll']").attr("checked",true);
}
}
);
$(":checkbox[name^='checkGroup']").click(
function(){
var perffix = 'checkItem';
var suffix = $(this).attr('name').split('_')[1];
var flag = this.checked;
$(":checkbox[name^='"+perffix+"'][name$='"+suffix+"']").each(
function(){
this.checked = flag;
}
);
}
);
$(":checkbox[name^='checkItem']").click(function(){
var suffix = $(this).attr('name').split('_')[1];
var itemLength = $(":checkbox[name^='checkItem'][name$='"+suffix+"']").length;
var itemCheckedLength = $(":checkbox[name^='checkItem'][name$='"+suffix+"']:checked").length;
if(itemCheckedLength == itemLength){
$(":checkbox[name^='checkGroup'][name$='"+suffix+"']").attr("checked",true);
}else{
$(":checkbox[name^='checkGroup'][name$='"+suffix+"']").attr("checked",false);
}
});
$(":checkbox[name!='checkAll']").click(function () {
var checkedLength = $(":checkbox[name!='checkAll']:checked").length;
if(checkedLength == totalLength){
$(":checkbox[name='checkAll']").attr("checked",true);
}else{
$(":checkbox[name='checkAll']").attr("checked",false);
}
});
/*全選*/
$(":checkbox[name^='checkAll']").click(
function () {
var flag = this.checked;
$(":checkbox").each(
function () {
this.checked = flag;
}
)
}
);
如果某個權限被選中,就將該權限Id加入permIdArray(權限數組)
var permIdArray = new Array();
$("#submitBtn").click(
function () {
var roleId = $("#roleId").val().trim();
var roleName = $("#roleName").val().trim();
var roleInfo = $("#roleInfo").val().trim();
var creatorName = 'admin';
console.info("date: "+getNowFormatDate());
console.info("roleId: "+roleId);
$(":checkbox[name^='checkItem']:checked").each(
function () {
permIdArray.push($(this).val());
console.info("permIdArray: "+$(this).val());
}
);
爲方便描述映射關係,這裏定義了一個MappingEntity(映射實體類)
public class MappingEntity {
private int primeMapId;//主映射(一對多的一方)
private int[] minorMapIdArray;//從映射(一對多的多方)
private String primeMapName;
private String primeMapInfo;
private String creatorName;
private Date createTime;
public int getPrimeMapId() {
return primeMapId;
}
public void setPrimeMapId(int primeMapId) {
this.primeMapId = primeMapId;
}
public int[] getMinorMapIdArray() {
return minorMapIdArray;
}
public void setMinorMapIdArray(int[] minorMapIdArray) {
this.minorMapIdArray = minorMapIdArray;
}
public String getPrimeMapName() {
return primeMapName;
}
public void setPrimeMapName(String primeMapName) {
this.primeMapName = primeMapName;
}
public String getPrimeMapInfo() {
return primeMapInfo;
}
public void setPrimeMapInfo(String primeMapInfo) {
this.primeMapInfo = primeMapInfo;
}
public String getCreatorName() {
return creatorName;
}
public void setCreatorName(String creatorName) {
this.creatorName = creatorName;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
勾選中後提交的請求
$.ajax({
url: [[@{/admin/assignPerm.asy}]],
type: 'post',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({primeMapId:roleId,minorMapIdArray : permIdArray,primeMapName : roleName,primeMapInfo : roleInfo,creatorName : creatorName}),
async: true,
success: function(data){
if(data != null){
alert(data.hintMsg);
if(data.operFlag == true){
window.location.reload();
}else{
$(".role_box").hide();
}
}
}
});
});
在分配權限的實現過程中,涉及到多次分配權限的問題。首先看看映射表是否存在該映射,如果不存在,說明是第一次分配權限,直接插入即可;如果存在,說明是不是第一次分配權限,要獲取上一次權限Id的list,比較前端傳過來的數組,將多餘的刪除,缺少的插入。通過ArrayCompare這個工具來實現
public class ArrayCompare {
/*第一個參數list獨有的部分*/
public static List<Integer> fristOwn(List<Integer> list,int[] array){
int[] array1 = listToArray(list);
List<Integer> fristOwnList=new ArrayList<>();
for (int num:array1) {
if (Arrays.binarySearch(array, num)<0){
fristOwnList.add(num);
}
}
return fristOwnList;
}
/*第二個參數list獨有的部分*/
public static List<Integer> secondOwn(List<Integer> list,int[] array){
int[] array1 = listToArray(list);
List<Integer> secondOwnList=new ArrayList<>();
for (int num:array) {
if (Arrays.binarySearch(array1, num)<0){
secondOwnList.add(num);
}
}
return secondOwnList;
}
public static int[] listToArray(List<Integer> list){
int[] array = new int[list.size()];
for(int i=0;i<list.size();i++){
array[i]=list.get(i);
}
return array;
}
}
權限分配的代碼邏輯
@PostMapping("/assignPerm.asy")
public void addPermMapping(@RequestBody MappingEntity mappingEntity, HttpServletResponse resp) throws Exception{
//時間格式
Date date=new Date();
Timestamp timeStamp = new Timestamp(date.getTime());
System.out.println(timeStamp.toString());
JSONObject jsonObject = new JSONObject();
String hintMsg = null;
boolean operFlag = false;
//查找之前是否存在映射關係
List<Integer> permIdList = permMappingService.permIdList(mappingEntity.getPrimeMapId());
int[] minorMapIdArray = mappingEntity.getMinorMapIdArray();
if(minorMapIdArray == null){
hintMsg = "前端傳遞的數據爲空!!!";
}else{
if(permIdList.size()==0){
//將role-perm映射關係插入permmapping表
List<PermMapping> permMappingList = new ArrayList<>();
for(int i=0;i<minorMapIdArray.length;i++){
//創建PermMapping對象,存放 role-perm 的映射信息
PermMapping permMapping = new PermMapping();
permMapping.setRoleId(mappingEntity.getPrimeMapId());
permMapping.setPermId(minorMapIdArray[i]);
permMapping.setCreatorName(mappingEntity.getCreatorName());
permMapping.setCreateTime(timeStamp);
//將role-perm 的映射信息存入permMappingList
permMappingList.add(permMapping);
}
permMappingService.addPermMappingList(permMappingList);
hintMsg = "插入數據成功!!!";
operFlag = true;
}else{
//需要刪除的部分
List<Integer> deleteList = ArrayCompare.fristOwn(permIdList, minorMapIdArray);
//需要添加的部分
List<Integer> insertList = ArrayCompare.secondOwn(permIdList, minorMapIdArray);
if(deleteList.size()>0){
ArrayList<PermMapping> permMappingList = new ArrayList<>();
for(Integer permId:deleteList){
PermMapping permMapping = new PermMapping();
permMapping.setPermId(permId);
permMapping.setRoleId(mappingEntity.getPrimeMapId());
permMappingList.add(permMapping);
}
permMappingService.deletePermMappingList(permMappingList);
}
if(insertList.size()>0){
List<PermMapping> permMappingList = new ArrayList<>();
for(int permId:insertList){
//創建PermMapping對象,存放 role-perm 的映射信息
PermMapping permMapping = new PermMapping();
permMapping.setRoleId(mappingEntity.getPrimeMapId());
permMapping.setPermId(permId);
permMapping.setCreatorName(mappingEntity.getCreatorName());
permMapping.setCreateTime(timeStamp);
//將role-perm 的映射信息存入permMappingList
permMappingList.add(permMapping);
}
permMappingService.addPermMappingList(permMappingList);
}
hintMsg = "插入數據成功!!!";
operFlag = true;
}
}
jsonObject.put("hintMsg",hintMsg);
jsonObject.put("operFlag",operFlag);
System.out.println(jsonObject.toJSONString());
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println(jsonObject.toJSONString());
resp.getWriter().close();
}
如果系統中的用戶與角色的關係也是1對N的話,給用戶分配角色的思路也是一樣的。
權限校驗
系統中除了登錄,註冊,退出操作以外的所有請求都要進行攔截(這裏我在考慮能否用網關攔截)。根據請求攜帶的session或cookie得到當前的用戶信息。去數據庫查詢該用戶的所有權限(建議將權限信息存入redis中),看看用戶是否有當前操作的權限,如果沒有,進行提示。