1. 程式人生 > >後臺許可權管理控制shiro+jsonMenu

後臺許可權管理控制shiro+jsonMenu

在學習後臺的許可權控制,自己參考了各種資料後的新手demo,ui框架選型為大家可以參照網上的,專案架構為spring mvc + spring+mybatis,簡單易用好上手!搭建好框架後開始了第一個任務,設計並實現一套簡單的許可權管理功能。

一套最基本的許可權管理包括使用者、角色、資源。

資料庫設計:

資源表:tb_menu (含選單項、選單欄、按鈕等)
角色表:tb_role(這裡我沒有建立中間表,所有的許可權通過組合的字串儲存)
使用者表:tb_users(關聯角色ID獲取資源許可權)

:1)、做法支援1對多,也就是使用者有多對多許可權控制,表的資料機構設計就靠大家自己構思啦,也可以參考我設計思路
2)、因是簡單的DEMO版本,所以service層和controller層寫在一起,大家實際做的時候,可不要模仿哦。

展示效果頁:
這裡寫圖片描述

實現程式碼如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head lang="en">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>許可權功能</title>

    <link
rel="stylesheet" href="${pageContext.request.contextPath}/js/ztree/zTreeStyle/demo.css" type="text/css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/js/ztree/zTreeStyle/zTreeStyle.css" type="text/css"> <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js"
>
</script> <script type="text/javascript" src="${pageContext.request.contextPath}/js/ztree/jquery-migrate-1.2.1.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/js/ztree/jquery.ztree.core-3.5.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/js/ztree/jquery.ztree.excheck-3.5.js"></script> </head> <body> <div class="form" align="center"> <form id="rolePowerForm" action="${pageContext.request.contextPath}/role/powerRole"> <input type="hidden" name="roleId" value="${role.roleId}"/> <input type="hidden" id="menuLinksId" name="menuLinksId" value="${role.menuLinksId}"/> <ul id="treeDemo" class="ztree"></ul><br> <!-- <input id="sumbitBut" type='button' value='確認'/> --> </form> </div> <script> $.browser={}; var zNodes = ${requestScope.roleMenuJsons} // console.log(zNodes); $(function(){ $.browser.msie=false; $.browser.version=0; if(navigator.userAgent.match(/MSIE ([0-9]+)./)){ $.browser.msie=true; $.browser.version=RegExp.$1; } $.fn.zTree.init($("#treeDemo"), setting, zNodes); }); var tree = ""; var setting = { check : { chkboxType:{"Y":"ps","N":"ps"},//勾選checkbox對於父子節點的關聯關係,取消勾選時不關聯父 chkStyle:"checkbox", enable : true //是否複選框 }, //資料 data : { simpleData : { enable : true, idKey: "id", pIdKey: "pid" } }, callback:{ onCheck:onCheck } }; //獲取選中節點 function onCheck(){ var treeObj=$.fn.zTree.getZTreeObj("treeDemo"); var nodes=treeObj.getCheckedNodes(true); var linkIds = ""; for(var i=0;i<nodes.length;i++){ linkIds += nodes[i].id+","; } var list = linkIds.substring(0,linkIds.length-1); $("#menuLinksId").val(list); } </script> </body> </html>

實體層 RoleMenuJson

package com.softAngel.admin.beans;

/**
 * 角色授權列表選單選項
 * @author Administrator
 *
 */
public class RoleMenuJson {

    /**  */
    private Integer id;
    /** 統一許可權節點 */
    private Integer pid;
    /** 功能名稱 */
    private String name;
    /** 選中狀態 */
    private boolean checked;
    /** 展開狀態 */
    private boolean open;


    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getPid() {
        return pid;
    }
    public void setPid(Integer pid) {
        this.pid = pid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public boolean isChecked() {
        return checked;
    }
    public void setChecked(boolean checked) {
        this.checked = checked;
    }
    public boolean isOpen() {
        return open;
    }
    public void setOpen(boolean open) {
        this.open = open;
    }


}

Action層

RolePermission

/**
     * 跳轉到分配許可權頁
     * @param res
     * @param id
     */
    @RequestMapping(value="/rePowerRole")
    public String rePowerRole(){
        @SuppressWarnings("unchecked")
        Map<String, Object> map = ReqToMap.toMap(request);
        String roleId = (String) map.get("roleId");
        SqlSession dao = getDao();
        Tbrole role = dao.selectOne("role.findTbRoleById",roleId);

        request.setAttribute("role", role);
        request.setAttribute("roleMenuJsons", JSONObject.toJSONString(getRoleMenuJsons(dao,role.getMenuLinksId())));
        return "/admin/role/rolePower";
    }

    /**
     * 獲取所有資源,並開啟擁有的資源許可權
     * @param dao
     * @param link
     * @return
     */
    public static List<RoleMenuJson> getRoleMenuJsons(SqlSession dao,String link){
        String []linkTemps = null;
        if(!StringUtil.isEmpty(link)){
            linkTemps = link.split(",");
        }
        List<RoleMenuJson> roleMenuJsons = new ArrayList<RoleMenuJson>();
        RoleMenuJson titleJson = new RoleMenuJson();
        titleJson.setId(0);
        titleJson.setPid(0);
        if(!StringUtil.isEmpty(link)){  //如果角色有許可權,總目錄應該選中
            titleJson.setChecked(true);
        }else{
            titleJson.setChecked(false);
        }
        List<TbmenuLink> menuList = dao.selectList("menuLink.menuForRoleList");     //獲取所有顯示的選單項
        for(int i = 0 ; i < menuList.size() ; i ++ ){
            TbmenuLink menu = menuList.get(i);
            RoleMenuJson single = new RoleMenuJson();   //建立節點
            single.setId(menu.getMenuId());
            single.setPid(menu.getParentId());
            single.setName(menu.getMenuName());
            if(linkTemps != null){
                for(String temp : linkTemps){
                    if(!StringUtil.isEmpty(temp)){
                        int number = Integer.parseInt(temp);
                        if(number == menu.getMenuId()){     //擁有的許可權功能點將被選中
                            single.setChecked(true);
                            single.setOpen(true);
                        }
                    }
                }
            }
            roleMenuJsons.add(single);
        }   
        titleJson.setName("許可權選單");
        titleJson.setOpen(true);
        roleMenuJsons.add(titleJson);//存放頭部選單

        return roleMenuJsons;
    }
    /**
     * 分配許可權
     * @param res
     * @param id
     */
    @RequestMapping(value="/powerRole")
    public void powerRole(HttpServletResponse res,
            @RequestParam(required=true)int roleId){
        JSONObject msg = new JSONObject();
        if(roleId > 0){
            @SuppressWarnings("unchecked")
            Map<String, Object> map = ReqToMap.toMap(request);
            SqlSession dao = getDao();
            Tbusers loginUser = null;
            try{
                loginUser = (Tbusers) session.getAttribute("loginUser");
                map.put("updater",loginUser.getId());
                int result = dao.update("role.updateRolePower",map);    //更新角色許可權
                if(result > 0){
                    msg.put("code", 0);
                    msg.put("info", "授權成功");
                }else{
                    msg.put("code", 1);
                    msg.put("info", "資料有誤,授權失敗");
                }
            }catch(Exception e){
                e.printStackTrace();
                logger.info("後臺未找到登入使用者");
                msg.put("code", 1);
                msg.put("info", "操作失敗,後臺未找到登入使用者");
            }
        }else{
            msg.put("code", 1);
            msg.put("info", "角色資訊錯誤");
        }
        out(msg.toJSONString(), res);
    }

登入時進行判斷操作,獲取有JSON許可權的選單欄
Menu 選單例項

package com.softAngel.admin.beans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;


/**
*@author leopard
*/
@SuppressWarnings("serial")
public class Menu implements Serializable{

    /**  */
    private Integer menuid;
    /** 欄目名 */
    private String menuname;
    /** 許可權型別(所屬父級,0本身) */
    private Integer menuParentId;
    /** 連結地址 */
    private String url;
    /** 排序 */
    private Integer menuSort;
    /** 選單欄圖示 */
    private String icon;

    /** 選單欄集合 */
    List<Menu> menus = new ArrayList<Menu>();

    public static List<Menu> formatTree(List<Menu> list) { 
        List<Menu> parentNodes = new ArrayList<Menu>();// parentnodes存放所有的父節點
        Menu node = new Menu();
        for(int i = 0 ; i < list.size() ; i ++){//尋找父級節點
            node = list.get(i);
            if(node.menuParentId == 0){
                parentNodes.add(node);//為tree root (父級選單)
            }
        }
        for(int i = 0 ; i < list.size() ; i ++){//檢視選單欄長度,迴圈資料
            node = list.get(i);
            for(int j = 0 ; j < parentNodes.size() ; j ++){//迴圈父節點資料
                Menu temp = parentNodes.get(j);//獲取父節點
                if(node.getMenuParentId() == temp.getMenuid()){//如果子節點的父級ID 和 父級節點的ID相同,歸屬到對應的父級節點下
                    temp.getMenus().add(node);//父節點新增對應的子節點ID
                }
            }
        }

        return parentNodes;
    }

    public Integer getMenuid() {
        return menuid;
    }
    public void setMenuid(Integer menuid) {
        this.menuid = menuid;
    }
    public String getMenuname() {
        return menuname;
    }
    public void setMenuname(String menuname) {
        this.menuname = menuname;
    }
    public Integer getMenuParentId() {
        return menuParentId;
    }
    public void setMenuParentId(Integer menuParentId) {
        this.menuParentId = menuParentId;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public Integer getMenuSort() {
        return menuSort;
    }
    public void setMenuSort(Integer menuSort) {
        this.menuSort = menuSort;
    }
    public String getIcon() {
        return icon;
    }
    public void setIcon(String icon) {
        this.icon = icon;
    }
    public List<Menu> getMenus() {
        return menus;
    }
    public void setMenus(List<Menu> menus) {
        this.menus = menus;
    }
}

UserAction

/**
     * 確認登入POST請求
     * @return
     */
    @RequestMapping(method ={RequestMethod.POST},value="/comit") 
    public String comit(HttpServletResponse res,
             @RequestParam(required=true) String pass,
             @RequestParam(required=true) String user,
                        final ModelMap model){
        Object obj=session.getAttribute("loginUser");
        String message="";
        if(obj==null){
                logger.debug("************ pass="+pass+",  user="+user);            
                @SuppressWarnings("rawtypes")
                Map map=ReqToMap.toMap(request);
                map.put("loginName",user);
                map.put("md5pass",MD5Crypter.encryptHEX(pass));
                SqlSession dao=getDao();
                Tbusers admin = dao.selectOne("user.checkLogin", map);
                if(admin==null){
                    message="賬號或密碼錯誤";
                    model.put("login_text", message);
                    return "/login/login";
                }else if(admin.getUserState() == 1){
                    message="此賬號不可用";
                    model.put("login_text", message);
                    return "/login/login";
                }else{
                    map.put("loginCount", admin.getLoginCount()+1);
                    map.put("id", admin.getId());
                    dao.update("user.updateLogin", map);
                    session.setAttribute("loginUser",admin);

                    /*** 查詢有許可權的選單欄列表 ***/
                    Map<String, Object> param = new HashMap<String, Object>();
                    List<TbmenuLink> menuLinks = null;
                    if(admin.getUserType()==1){
                        menuLinks = dao.selectList("power.menuLinks",param);
                    }else if(admin.getUserType()==0){
                        String rolePower = getRolePowerList(dao,admin.getRoleId());
                        if(!StringUtil.isEmpty(rolePower)){
                            param.put("rolePower", rolePower);
                            menuLinks = dao.selectList("power.menuLinks",param);//獲取所有選單欄和選單項,除按鈕外
                        }
                    }
                    List<Menu> menuTemp = new ArrayList<Menu>();
                    if(menuLinks != null){
                        for(int i = 0 ;i<menuLinks.size();i++){
                            Menu menu = new Menu();
                            menu.setMenuid(menuLinks.get(i).getMenuId());
                            menu.setMenuname(menuLinks.get(i).getMenuName());
                            menu.setMenuParentId(menuLinks.get(i).getParentId());
                            menu.setUrl(request.getContextPath()+menuLinks.get(i).getMenuUrl());
//                          System.out.println(request.getContextPath());//專案根路徑
                            if(menuLinks.get(i).getMenuNodeType()==0){//父級選單
                                menu.setIcon("icon-sys");
                            }else{//二級選單
                                menu.setIcon("icon-nav");
                            }
                            menuTemp.add(menu);
                        }
                    }
                    List<Menu> menus = Menu.formatTree(menuTemp);
                    session.setAttribute("menus", JSONObject.toJSONString(menus));
                    return "/login/main";
                }

        }else{
            return "/login/main";
        }
    }

前臺頁面展示

JS展示。。JsonMENU
var json = ${sessionScope.menus};
var _menus ={"menus":json};

舊式原先資料展示:
var _menus = {"menus":[
                    {"menuid":"1","icon":"icon-sys","menuname":"使用者管理",
                        "menus":[
                            {"menuid":"11","menuname":"使用者列表1","icon":"icon-nav",    "url":"${pageContext.request.contextPath}/user/toUserList"},
                        ]
                    }
            ]
};

以上可實現獲取有許可權的選單項,後臺許可權角色分配就略過,直接上效果圖
這裡寫圖片描述

許可權分配好後,用許可權使用者登入,只能看到有許可權的一欄選單欄和對應的許可權選單項

這裡寫圖片描述

對於按鈕,下面以shiro來講解如何設定按鈕許可權

頁面展示的按鈕由shiro許可權標籤控制

WEB-INF目錄下建立資料夾tlds 建立自定義標籤檔案shiros.tld,我們通過自定義標籤實現頁面按鈕的控制。

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
    <!-- 描述 -->
    <description>p2p permission taglib</description>  
    <display-name>permission taglib</display-name>  
    <!-- 版本號 -->
    <tlib-version>1.0</tlib-version>  
    <!-- 標籤短名 -->
    <short-name>admin</short-name>  
    <uri>http://servenboy.cn/</uri>  <!--不是必須-->

    <tag>  
        <description>許可權校驗標籤,有許可權就顯示標籤體的內容,否則不顯示</description>  <!-- 描述 -->
        <name>permission</name>  
        <tag-class>com.softAngel.admin.beans.PermissionTag</tag-class>  
        <body-content>JSP</body-content>   <!--這裡如果設為empty,則無body-->
        <attribute>  
            <description></description>  
            <name>module</name> <!-- 屬性名-->
            <required>true</required>  <!--標籤的屬性是否是必須的-->
            <rtexprvalue>false</rtexprvalue>  
        </attribute>  
        <attribute>  
            <description></description>  
            <name>code</name>  
            <required>true</required>  
            <rtexprvalue>false</rtexprvalue>  
        </attribute>  
    </tag>  
</taglib>

自定義標籤類

package com.softAngel.admin.beans;

import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.ibatis.session.SqlSession;

import com.softAngel.admin.db.StaticFactory;
import com.softAngel.admin.web.action.user.UserAction;

public class PermissionTag extends TagSupport{

    public static  final ThreadLocal<SqlSession> localDAO =new ThreadLocal<SqlSession>();

    private String module;

    private String code;


    @Override
    public int doStartTag() throws JspException {
        boolean result = false;
        HttpSession session = pageContext.getSession();
        //CusPermission.getSessionUser()
        Tbusers user = (Tbusers) session.getAttribute("loginUser");
        if(user!=null ){
            if(user.getLoginName().equals("admin")){
                result = true;
            }else{
                String roleMenuLinkList = UserAction.getRolePowerList(getDao(), user.getRoleId());
                if(roleMenuLinkList!=null){
                    String LinkIds[] = roleMenuLinkList.split(",");
                    for(String temp : LinkIds){
                        if(temp.equals(code)){
                            result = true;
                            break;
                        }
                    }
                }
            }
        }
        //  真:返回EVAL_BODY_INCLUDE(執行標籤);假:返回SKIP_BODY(跳過標籤不執行)
        return result ? EVAL_BODY_INCLUDE : SKIP_BODY;
    }

    public String getModule() {
        return module;
    }

    public void setModule(String module) {
        this.module = module;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    protected SqlSession getDao(){
        SqlSession dao=localDAO.get();
        if(dao==null){  
            dao=StaticFactory.getSqlSession(true);
            localDAO.set(dao);
        }
        return dao;
    }
}

在web.xml中配置許可權類引用路徑

    <jsp-config>
        <taglib>
            <taglib-uri>/adminPower</taglib-uri>
            <taglib-location>/WEB-INF/tlds/shiros.tld</taglib-location>
        </taglib>
    </jsp-config>

頁面頭部直接引入

<%@ taglib uri="/adminPower" prefix="pr" %>

在頁面需要展示的地方包裹標籤<.pr : permission code=”2”>

<div class="btnList">
      <pr:permission module="1" code="2"><input type="button" value='測試1-1'/></pr:permission>
      <pr:permission module="1" code="3"><input type="button" value='測試1-2'/></pr:permission>
      <pr:permission module="1" code="4"><input type="button" value='測試1-3'/></pr:permission>        
</div>

以上就是該許可權管理中許可權樹(為角色分配許可權)的大體實現。

簡單詳解:<.pr : permission module=”1” code=”2”> 中的module 代表 資源ID為1的 角色管理選單欄 (也就是所謂的父級) code代表資源ID 為2的 按鈕(父級選單下對應的子級選單欄項或按鈕),也可理解為許可權資源裡面同時含有 1,2這兩個ID的許可權,按鈕-測試1-1 就會顯示出來,否則為隱藏不可見